def test_dpp_sampler_generic_kernel(self):
        """ Test whether 'Chol' sampling mode generates samples with the right 1 and 2 points inclusion probabilities when DPP defined by orthogonal projection correlation kernel K
        .. seealso::

            - :cite:`Pou19` Algorithm 1
        """
        self.list_of_samples = [
            dpp_sampler_generic_kernel(self.K)[0]
            for _ in range(self.nb_samples)
        ]

        self.assertTrue(self.singleton_adequation())
        self.assertTrue(self.doubleton_adequation())
Exemplo n.º 2
0
    def sample_exact(self, mode='GS', random_state=None):
        """ Sample exactly from the corresponding :class:`FiniteDPP <FiniteDPP>` object. The sampling scheme is based on the chain rule with Gram-Schmidt like updates of the conditionals.

        :param mode:

            - ``projection=True``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of :math:`\\mathbf{K}`.
                - ``'Chol'`` :cite:`Pou19` Algorithm 3
                - ``'Schur'``: when DPP defined from correlation kernel ``K``, use Schur complement to compute conditionals

            - ``projection=False``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of the eigenvectors of :math:`\\mathbf{K}` selected in Phase 1.
                - ``'GS_bis'``: Slight modification of ``'GS'``
                - ``'Chol'`` :cite:`Pou19` Algorithm 1
                - ``'KuTa12'``: Algorithm 1 in :cite:`KuTa12`
        :type mode:
            string, default ``'GS'``

        :return:
            A sample from the corresponding :class:`FiniteDPP <FiniteDPP>` object.
        :rtype:
            array_like

        .. note::

            Each time you call this function, the sample is added to the :py:attr:`~FiniteDPP.list_of_samples`.

            The latter can be emptied using :py:meth:`~FiniteDPP.flush_samples`

        .. caution::

            The underlying kernel :math:`\\mathbf{K}`, resp. :math:`\\mathbf{L}` must be real valued for now.

        .. seealso::

            - :ref:`finite_dpps_exact_sampling`
            - :py:meth:`~FiniteDPP.flush_samples`
            - :py:meth:`~FiniteDPP.sample_mcmc`
        """

        rng = check_random_state(random_state)

        self.sampling_mode = mode

        if self.sampling_mode == 'Schur':
            if self.kernel_type == 'correlation' and self.projection:
                self.compute_K()
                sampl = proj_dpp_sampler_kernel(self.K,
                                                self.sampling_mode,
                                                random_state=rng)
                self.list_of_samples.append(sampl)
            else:
                err_print =\
                    ['`Schur` sampling mode is only available for projection DPPs, i.e., `kernel_type="correlation"` and `projection=True`',
                     'Given: {}'.format((self.kernel_type, self.projection))]
                raise ValueError('\n'.join(err_print))

        elif self.sampling_mode == 'Chol':
            self.compute_K()
            if self.kernel_type == 'correlation' and self.projection:
                sampl = proj_dpp_sampler_kernel(self.K,
                                                self.sampling_mode,
                                                random_state=rng)
            else:
                sampl, _ = dpp_sampler_generic_kernel(self.K, random_state=rng)
            self.list_of_samples.append(sampl)

        # If eigen decoposition of K, L or L_dual is available USE IT!
        elif self.K_eig_vals is not None:
            # Phase 1
            if self.kernel_type == 'correlation' and self.projection:
                V = self.eig_vecs[:, self.K_eig_vals > 0.5]
            else:
                V = dpp_eig_vecs_selector(self.K_eig_vals,
                                          self.eig_vecs,
                                          random_state=rng)
            # Phase 2
            if V.shape[1]:
                sampl = proj_dpp_sampler_eig(V,
                                             self.sampling_mode,
                                             random_state=rng)
            else:
                sampl = np.array([])
            self.list_of_samples.append(sampl)

        elif self.L_eig_vals is not None:
            self.K_eig_vals = self.L_eig_vals / (1.0 + self.L_eig_vals)
            self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.L_dual_eig_vals is not None:
            # Phase 1
            V = dpp_eig_vecs_selector_L_dual(self.L_dual_eig_vals,
                                             self.L_dual_eig_vecs,
                                             self.L_gram_factor,
                                             random_state=rng)
            # Phase 2
            sampl = proj_dpp_sampler_eig(V,
                                         self.sampling_mode,
                                         random_state=rng)
            self.list_of_samples.append(sampl)

        # If DPP defined via projection correlation kernel K
        # no eigendecomposition required
        elif (self.K is not None) and self.projection:
            sampl = proj_dpp_sampler_kernel(self.K,
                                            self.sampling_mode,
                                            random_state=rng)
            self.list_of_samples.append(sampl)

        elif self.L_dual is not None:
            self.L_dual_eig_vals, self.L_dual_eig_vecs =\
                la.eigh(self.L_dual)
            self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.K is not None:
            self.K_eig_vals, self.eig_vecs = la.eigh(self.K)
            self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.L is not None:
            self.L_eig_vals, self.eig_vecs = la.eigh(self.L)
            self.sample_exact(mode=self.sampling_mode, random_state=rng)

        # If DPP defined through correlation kernel with parameter 'A_zono'
        # a priori you wish to use the zonotope approximate sampler
        elif self.A_zono is not None:
            warn(
                'DPP defined via `A_zono`, apriori you want to use `sample_mcmc`, but you have called `sample_exact`'
            )

            self.K_eig_vals = np.ones(self.A_zono.shape[0])
            self.eig_vecs, _ = la.qr(self.A_zono.T, mode='economic')

            self.sample_exact(self.sampling_mode, random_state=rng)
Exemplo n.º 3
0
    def sample_exact(self, mode='GS', **params):
        """ Sample exactly from the corresponding :class:`FiniteDPP <FiniteDPP>` object.

        :param mode:

            - ``projection=True``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of :math:`\\mathbf{K}`.
                - ``'Chol'`` :cite:`Pou19` Algorithm 3
                - ``'Schur'``: when DPP defined from correlation kernel ``K``, use Schur complement to compute conditionals

            - ``projection=False``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of the eigenvectors of :math:`\\mathbf{K}` selected in Phase 1.
                - ``'GS_bis'``: Slight modification of ``'GS'``
                - ``'Chol'`` :cite:`Pou19` Algorithm 1
                - ``'KuTa12'``: Algorithm 1 in :cite:`KuTa12`
                - ``'vfx'``: the dpp-vfx rejection sampler in :cite:`DeCaVa19`

        :type mode:
            string, default ``'GS'``

        :param dict params:
            Dictionary containing the parameters for exact samplers with keys

            - ``'random_state'`` (default None)
            - If ``mode='vfx'``

                See :py:meth:`~dppy.exact_sampling.dpp_vfx_sampler` for a full list of all parameters accepted by 'vfx' sampling. We report here the most impactful

                + ``'rls_oversample_dppvfx'`` (default 4.0) Oversampling parameter used to construct dppvfx's internal Nystrom approximation. This makes each rejection round slower and more memory intensive, but reduces variance and the number of rounds of rejections.
                + ``'rls_oversample_bless'`` (default 4.0) Oversampling parameter used during bless's internal Nystrom approximation. This makes the one-time pre-processing slower and more memory intensive, but reduces variance and the number of rounds of rejections

                Empirically, a small factor [2,10] seems to work for both parameters. It is suggested to start with a small number and increase if the algorithm fails to terminate.

        :return:
            Returns a sample from the corresponding :class:`FiniteDPP <FiniteDPP>` object. In any case, the sample is appended to the :py:attr:`~FiniteDPP.list_of_samples` attribute as a list.

        :rtype:
            list

        .. note::

            Each time you call this method, the sample is appended to the :py:attr:`~FiniteDPP.list_of_samples` attribute as a list.

            The :py:attr:`~FiniteDPP.list_of_samples` attribute can be emptied using :py:meth:`~FiniteDPP.flush_samples`

        .. caution::

            The underlying kernel :math:`\\mathbf{K}`, resp. :math:`\\mathbf{L}` must be real valued for now.

        .. seealso::

            - :ref:`finite_dpps_exact_sampling`
            - :py:meth:`~FiniteDPP.flush_samples`
            - :py:meth:`~FiniteDPP.sample_mcmc`
        """

        rng = check_random_state(params.get('random_state', None))

        self.sampling_mode = mode

        if self.sampling_mode == 'Schur':
            if self.kernel_type == 'correlation' and self.projection:
                self.compute_K()
                sampl = proj_dpp_sampler_kernel(self.K,
                                                self.sampling_mode,
                                                random_state=rng)
            else:
                err_print =\
                    ['`Schur` sampling mode is only available for projection DPPs, i.e., `kernel_type="correlation"` and `projection=True`',
                     'Given: {}'.format((self.kernel_type, self.projection))]
                raise ValueError('\n'.join(err_print))

        elif self.sampling_mode == 'Chol':
            self.compute_K()
            if self.kernel_type == 'correlation' and self.projection:
                sampl = proj_dpp_sampler_kernel(self.K,
                                                self.sampling_mode,
                                                random_state=rng)
            else:
                sampl, _ = dpp_sampler_generic_kernel(self.K, random_state=rng)

        elif self.sampling_mode == 'vfx':
            if self.eval_L is None or self.X_data is None:
                raise ValueError(
                    'The vfx sampler is currently only available with '
                    '{"L_eval_X_data": (L_eval, X_data)} representation.')

            params.pop("random_state", None)
            sampl, self.intermediate_sample_info = dpp_vfx_sampler(
                self.intermediate_sample_info,
                self.X_data,
                self.eval_L,
                random_state=rng,
                **params)

        # If eigen decoposition of K, L or L_dual is available USE IT!
        elif self.K_eig_vals is not None:
            # Phase 1
            if self.kernel_type == 'correlation' and self.projection:
                V = self.eig_vecs[:, self.K_eig_vals > 0.5]
            else:
                V = dpp_eig_vecs_selector(self.K_eig_vals,
                                          self.eig_vecs,
                                          random_state=rng)
            # Phase 2
            sampl = proj_dpp_sampler_eig(V,
                                         self.sampling_mode,
                                         random_state=rng)

        # elif self.L_dual_eig_vals is not None:
        #     # Phase 1
        #     V = dpp_eig_vecs_selector_L_dual(self.L_dual_eig_vals,
        #                                      self.L_dual_eig_vecs,
        #                                      self.L_gram_factor,
        #                                      random_state=rng)
        #     # Phase 2
        #     sampl = proj_dpp_sampler_eig(V, self.sampling_mode,
        #                                  random_state=rng)
        #

        elif self.L_eig_vals is not None:
            self.K_eig_vals = self.L_eig_vals / (1.0 + self.L_eig_vals)
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.L_dual is not None:
            # L_dual = Phi Phi.T = W Theta W.T
            # L = Phi.T Phi = V Gamma V
            # implies Gamma = Theta and V = Phi.T W Theta^{-1/2}
            self.L_eig_vals, L_dual_eig_vecs = la.eigh(self.L_dual)
            self.L_eig_vals = is_geq_0(self.L_eig_vals)
            self.eig_vecs = self.L_gram_factor.T.dot(L_dual_eig_vecs /
                                                     np.sqrt(self.L_eig_vals))
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        # If DPP defined via projection correlation kernel K
        # no eigendecomposition required
        elif self.K is not None and self.projection:
            sampl = proj_dpp_sampler_kernel(self.K,
                                            self.sampling_mode,
                                            random_state=rng)

        elif self.K is not None:
            self.K_eig_vals, self.eig_vecs = la.eigh(self.K)
            self.K_eig_vals = is_in_01(self.K_eig_vals)
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.L is not None:
            self.L_eig_vals, self.eig_vecs = la.eigh(self.L)
            self.L_eig_vals = is_geq_0(self.L_eig_vals)
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        # If DPP defined through correlation kernel with parameter 'A_zono'
        # a priori you wish to use the zonotope approximate sampler
        elif self.A_zono is not None:
            warn(
                'DPP defined via `A_zono`, apriori you want to use `sample_mcmc`, but you have called `sample_exact`'
            )

            self.K_eig_vals = np.ones(self.A_zono.shape[0])
            self.eig_vecs, _ = la.qr(self.A_zono.T, mode='economic')
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        elif self.eval_L is not None and self.X_data is not None:
            self.compute_L()
            return self.sample_exact(mode=self.sampling_mode, random_state=rng)

        else:
            raise ValueError(
                'None of the available samplers could be used based on the current DPP representation. This should never happen, please consider rasing an issue on github at https://github.com/guilgautier/DPPy/issues'
            )

        self.list_of_samples.append(sampl)
        return sampl
Exemplo n.º 4
0
    def sample_exact(self, mode='GS'):
        """ Sample exactly from the corresponding :class:`FiniteDPP <FiniteDPP>` object. The sampling scheme is based on the chain rule with Gram-Schmidt like updates of the conditionals.

        :param mode:

            - ``projection=True``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of :math:`\mathbf{K}`.
                - ``'Schur'``: Use Schur complement to compute conditionals.

            - ``projection=False``:
                - ``'GS'`` (default): Gram-Schmidt on the rows of the eigenvectors of :math:`\mathbf{K}` selected in Phase 1.
                - ``'GS_bis'``: Slight modification of ``'GS'``
                - ``'KuTa12'``: Algorithm 1 in :cite:`KuTa12`
        :type mode:
            string, default ``'GS'``

        :return:
            A sample from the corresponding :class:`FiniteDPP <FiniteDPP>` object.
        :rtype:
            array_like

        .. note::

            Each time you call this function, the sample is added to the :py:attr:`~FiniteDPP.list_of_samples`.

            The latter can be emptied using :py:meth:`~FiniteDPP.flush_samples`

        .. caution::

            The underlying kernel :math:`\mathbf{K}`, resp. :math:`\mathbf{L}` must be real valued for now.

        .. seealso::

            - :ref:`finite_dpps_exact_sampling`
            - :py:meth:`~FiniteDPP.flush_samples`
            - :py:meth:`~FiniteDPP.sample_mcmc`
        """

        self.sampling_mode = mode

        if self.sampling_mode == 'Chol':
            self.compute_K()
            if self.projection:
                sampl = proj_dpp_sampler_kernel(self.K, self.sampling_mode)
            else:
                sampl = dpp_sampler_generic_kernel(self.K)[0]
            self.list_of_samples.append(sampl)

        # If eigen decoposition of K, L or L_dual is available USE IT!
        elif self.K_eig_vals is not None:
            # Phase 1
            V = dpp_eig_vecs_selector(self.K_eig_vals, self.eig_vecs)
            # Phase 2
            if V.shape[1]:
                sampl = proj_dpp_sampler_eig(V, self.sampling_mode)
            else:
                sampl = np.array([])
            self.list_of_samples.append(sampl)

        elif self.L_eig_vals is not None:
            self.K_eig_vals = self.L_eig_vals / (1.0 + self.L_eig_vals)
            self.sample_exact(self.sampling_mode)

        elif self.L_dual_eig_vals is not None:
            # Phase 1
            V = dpp_eig_vecs_selector_L_dual(self.L_dual_eig_vals,
                                             self.L_dual_eig_vecs,
                                             self.L_gram_factor)
            # Phase 2
            sampl = proj_dpp_sampler_eig(V, self.sampling_mode)
            self.list_of_samples.append(sampl)

        # If DPP defined via projection correlation kernel K
        # no eigendecomposition required
        elif (self.K is not None) and self.projection:
            sampl = proj_dpp_sampler_kernel(self.K, self.sampling_mode)
            self.list_of_samples.append(sampl)

        elif self.L_dual is not None:
            self.L_dual_eig_vals, self.L_dual_eig_vecs =\
                la.eigh(self.L_dual)
            self.sample_exact(self.sampling_mode)

        elif self.K is not None:
            self.K_eig_vals, self.eig_vecs = la.eigh(self.K)
            self.sample_exact(self.sampling_mode)

        elif self.L is not None:
            self.L_eig_vals, self.eig_vecs = la.eigh(self.L)
            self.sample_exact(self.sampling_mode)

        # If DPP defined through correlation kernel with parameter 'A_zono'
        # a priori you wish to use the zonotope approximate sampler
        elif self.A_zono:
            warn(
                'DPP defined via `A_zono`, apriori you want to use `sampl_mcmc`, but you have called `sample_exact`'
            )

            self.K_eig_vals = np.ones(self.A_zono.shape[0])
            self.eig_vecs, _ = la.qr(self.A_zono.T, mode='economic')

            self.sample_exact(self.sampling_mode)