# Path: diffprivlib/models/utils.py
# def covariance_eig(array, epsilon=1.0, norm=None, dims=None, eigvals_only=False):
#     r"""
#     Return the eigenvalues and eigenvectors of the covariance matrix of `array`, satisfying differential privacy.
# 
#     Paper link: http://papers.nips.cc/paper/9567-differentially-private-covariance-estimation.pdf
# 
#     Parameters
#     ----------
#     array : array-like, shape (n_samples, n_features)
#         Matrix for which the covariance matrix is sought.
# 
#     epsilon : float, default: 1.0
#         Privacy parameter :math:`\epsilon`.
# 
#     norm : float, optional
#         The max l2 norm of any row of the input array.  This defines the spread of data that will be protected by
#         differential privacy.
# 
#         If not specified, the max norm is taken from the data, but will result in a :class:`.PrivacyLeakWarning`, as it
#         reveals information about the data.  To preserve differential privacy fully, `norm` should be selected
#         independently of the data, i.e. with domain knowledge.
# 
#     dims : int, optional
#         Number of eigenvectors to return.  If `None`, return all eigenvectors.
# 
#     eigvals_only : bool, default: False
#         Only return the eigenvalue estimates.  If True, all the privacy budget is spent on estimating the eigenvalues.
# 
#     Returns
#     -------
#     w : (n_features) array
#         The eigenvalues, each repeated according to its multiplicity.
# 
#     v : (n_features, dims) array
#         The normalized (unit "length") eigenvectors, such that the column ``v[:,i]`` is the eigenvector corresponding to
#         the eigenvalue ``w[i]``.
# 
#     """
# 
#     n_features = array.shape[1]
#     dims = n_features if dims is None else min(dims, n_features)
#     if not isinstance(dims, Integral):
#         raise TypeError(f"Number of requested dimensions must be integer-valued, got {type(dims)}")
#     if dims < 0:
#         raise ValueError(f"Number of requested dimensions must be non-negative, got {dims}")
# 
#     max_norm = np.linalg.norm(array, axis=1).max()
#     if norm is None:
#         warnings.warn("Data norm has not been specified and will be calculated on the data provided.  This will result "
#                       "in additional privacy leakage. To ensure differential privacy and no additional privacy "
#                       "leakage, specify `data_norm` at initialisation.", PrivacyLeakWarning)
#         norm = max_norm
#     elif max_norm > norm and not np.isclose(max_norm, norm):
#         raise ValueError(f"Rows of input array must have l2 norm of at most {norm}, got {max_norm}")
# 
#     cov = array.T.dot(array) / (norm ** 2)
#     eigvals = np.sort(np.linalg.eigvalsh(cov))[::-1]
#     epsilon_0 = epsilon if eigvals_only else epsilon / (dims + (dims != n_features))
# 
#     mech_eigvals = LaplaceBoundedDomain(epsilon=epsilon_0, lower=0, upper=float("inf"), sensitivity=2)
#     noisy_eigvals = np.array([mech_eigvals.randomise(eigval) for eigval in eigvals]) * (norm ** 2)
# 
#     if eigvals_only:
#         return noisy_eigvals
# 
#     # When estimating all eigenvectors, we don't need to spend budget for the dth vector
#     epsilon_i = epsilon / (dims + (dims != n_features))
#     cov_i = cov
#     proj_i = np.eye(n_features)
# 
#     theta = np.zeros((0, n_features))
#     mech_cov = Bingham(epsilon=epsilon_i)
# 
#     for _ in range(dims):
#         if cov_i.size > 1:
#             u_i = mech_cov.randomise(cov_i)
#         else:
#             u_i = np.ones((1,))
# 
#         theta_i = proj_i.T.dot(u_i)
#         theta = np.vstack((theta, theta_i))
# 
#         if cov_i.size > 1:
#             proj_i = null_space(theta).T
#             cov_i = proj_i.dot(cov).dot(proj_i.T)
# 
#     return noisy_eigvals, theta.T
# 
# Path: diffprivlib/utils.py
# class PrivacyLeakWarning(RuntimeWarning):
#     """Custom warning to capture privacy leaks resulting from incorrect parameter setting.
# 
#     For example, this warning may occur when the user:
# 
#         - fails to specify the bounds or range of data to a model where required (e.g., `bounds=None` to
#           :class:`.GaussianNB`).
#         - inputs data to a model that falls outside the bounds or range originally specified.
# 
#     """


from unittest import TestCase
from diffprivlib.models.utils import covariance_eig
from diffprivlib.utils import PrivacyLeakWarning
import numpy as np
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=1)), 1))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=0)), 1))

    def test_large_norm(self):
        d, n = 3, 10
        data = self.generate_normed_data(d, n)
        data *= 2

        dp_vals, dp_vecs = covariance_eig(data, epsilon=float("inf"), norm=2)
        vals, vecs = np.linalg.eigh(data.T.dot(data))

        self.assertTrue(np.allclose(vals[vals.argsort()], dp_vals[dp_vals.argsort()]))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=1)), 1))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=0)), 1))

    def test_bad_norm(self):
        d, n = 3, 10
        data = self.generate_normed_data(d, n)
        data *= 2

        with self.assertWarns(PrivacyLeakWarning):
            covariance_eig(data, epsilon=float("inf"), norm=None)

        with self.assertRaises(ValueError):
            covariance_eig(data, epsilon=float("inf"), norm=1)

    def test_dims(self):
        d, n = 5, 10
        data = self.generate_normed_data(d, n)


# Path: diffprivlib/models/utils.py
# def covariance_eig(array, epsilon=1.0, norm=None, dims=None, eigvals_only=False):
#     r"""
#     Return the eigenvalues and eigenvectors of the covariance matrix of `array`, satisfying differential privacy.
# 
#     Paper link: http://papers.nips.cc/paper/9567-differentially-private-covariance-estimation.pdf
# 
#     Parameters
#     ----------
#     array : array-like, shape (n_samples, n_features)
#         Matrix for which the covariance matrix is sought.
# 
#     epsilon : float, default: 1.0
#         Privacy parameter :math:`\epsilon`.
# 
#     norm : float, optional
#         The max l2 norm of any row of the input array.  This defines the spread of data that will be protected by
#         differential privacy.
# 
#         If not specified, the max norm is taken from the data, but will result in a :class:`.PrivacyLeakWarning`, as it
#         reveals information about the data.  To preserve differential privacy fully, `norm` should be selected
#         independently of the data, i.e. with domain knowledge.
# 
#     dims : int, optional
#         Number of eigenvectors to return.  If `None`, return all eigenvectors.
# 
#     eigvals_only : bool, default: False
#         Only return the eigenvalue estimates.  If True, all the privacy budget is spent on estimating the eigenvalues.
# 
#     Returns
#     -------
#     w : (n_features) array
#         The eigenvalues, each repeated according to its multiplicity.
# 
#     v : (n_features, dims) array
#         The normalized (unit "length") eigenvectors, such that the column ``v[:,i]`` is the eigenvector corresponding to
#         the eigenvalue ``w[i]``.
# 
#     """
# 
#     n_features = array.shape[1]
#     dims = n_features if dims is None else min(dims, n_features)
#     if not isinstance(dims, Integral):
#         raise TypeError(f"Number of requested dimensions must be integer-valued, got {type(dims)}")
#     if dims < 0:
#         raise ValueError(f"Number of requested dimensions must be non-negative, got {dims}")
# 
#     max_norm = np.linalg.norm(array, axis=1).max()
#     if norm is None:
#         warnings.warn("Data norm has not been specified and will be calculated on the data provided.  This will result "
#                       "in additional privacy leakage. To ensure differential privacy and no additional privacy "
#                       "leakage, specify `data_norm` at initialisation.", PrivacyLeakWarning)
#         norm = max_norm
#     elif max_norm > norm and not np.isclose(max_norm, norm):
#         raise ValueError(f"Rows of input array must have l2 norm of at most {norm}, got {max_norm}")
# 
#     cov = array.T.dot(array) / (norm ** 2)
#     eigvals = np.sort(np.linalg.eigvalsh(cov))[::-1]
#     epsilon_0 = epsilon if eigvals_only else epsilon / (dims + (dims != n_features))
# 
#     mech_eigvals = LaplaceBoundedDomain(epsilon=epsilon_0, lower=0, upper=float("inf"), sensitivity=2)
#     noisy_eigvals = np.array([mech_eigvals.randomise(eigval) for eigval in eigvals]) * (norm ** 2)
# 
#     if eigvals_only:
#         return noisy_eigvals
# 
#     # When estimating all eigenvectors, we don't need to spend budget for the dth vector
#     epsilon_i = epsilon / (dims + (dims != n_features))
#     cov_i = cov
#     proj_i = np.eye(n_features)
# 
#     theta = np.zeros((0, n_features))
#     mech_cov = Bingham(epsilon=epsilon_i)
# 
#     for _ in range(dims):
#         if cov_i.size > 1:
#             u_i = mech_cov.randomise(cov_i)
#         else:
#             u_i = np.ones((1,))
# 
#         theta_i = proj_i.T.dot(u_i)
#         theta = np.vstack((theta, theta_i))
# 
#         if cov_i.size > 1:
#             proj_i = null_space(theta).T
#             cov_i = proj_i.dot(cov).dot(proj_i.T)
# 
#     return noisy_eigvals, theta.T
# 
# Path: diffprivlib/utils.py
# class PrivacyLeakWarning(RuntimeWarning):
#     """Custom warning to capture privacy leaks resulting from incorrect parameter setting.
# 
#     For example, this warning may occur when the user:
# 
#         - fails to specify the bounds or range of data to a model where required (e.g., `bounds=None` to
#           :class:`.GaussianNB`).
#         - inputs data to a model that falls outside the bounds or range originally specified.
# 
#     """


# Path: tests/models/test_covariance_eig.py
from unittest import TestCase
from diffprivlib.models.utils import covariance_eig
from diffprivlib.utils import PrivacyLeakWarning
import numpy as np

        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=1)), 1))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=0)), 1))

    def test_large_norm(self):
        d, n = 3, 10
        data = self.generate_normed_data(d, n)
        data *= 2

        dp_vals, dp_vecs = covariance_eig(data, epsilon=float("inf"), norm=2)
        vals, vecs = np.linalg.eigh(data.T.dot(data))

        self.assertTrue(np.allclose(vals[vals.argsort()], dp_vals[dp_vals.argsort()]))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=1)), 1))
        self.assertTrue(np.allclose(abs(dp_vecs.T.dot(vecs).sum(axis=0)), 1))

    def test_bad_norm(self):
        d, n = 3, 10
        data = self.generate_normed_data(d, n)
        data *= 2

        with self.assertWarns(PrivacyLeakWarning):
            covariance_eig(data, epsilon=float("inf"), norm=None)

        with self.assertRaises(ValueError):
            covariance_eig(data, epsilon=float("inf"), norm=1)

    def test_dims(self):
        d, n = 5, 10
        data = self.generate_normed_data(d, n)


        vals, vecs = covariance_eig(data, norm=1, dims=3)