Example #1
0
    def test_blockdiag_approx_on_blockdiag(self):

        # Check that blockdiag approx yields same solution
        # for block-diagonal matrix.
        p = 250
        rho = 0.3
        gamma = 0
        groups = np.arange(1, p + 1, 1)
        dgprocess = dgp.DGP()
        _, _, _, _, V = dgprocess.sample_data(p=p,
                                              method="blockequi",
                                              gamma=gamma,
                                              rho=rho)

        for method in ["mvr", "maxent", "sdp"]:
            # Check that S_approx is valid
            S_approx = smatrix.compute_smatrix(V, method=method, max_block=10)
            self.check_S_properties(V, S_approx, groups)

            # In this case, should be the same as S_exact
            S_exact = smatrix.compute_smatrix(V, method=method)
            np.testing.assert_array_almost_equal(
                S_approx,
                S_exact,
                2,
                err_msg=
                "Blockdiag approximation yields incorrect answer for blockdiag Sigma",
            )
Example #2
0
    def test_agreement_on_factor_models(self):

        # Random factor models
        np.random.seed(110)
        ks = [1, 10, 20, 50]
        p = 150
        for k in ks:
            for method in ['mvr', 'maxent', 'sdp']:
                D = np.random.uniform(low=0.01, high=1, size=(p, ))
                U = np.random.randn(p, k) / np.sqrt(k)
                # Rescale to correlation matrix
                diag_Sigma = D + (np.power(U, 2)).sum(axis=1)
                D = D / diag_Sigma
                U = U / np.sqrt(diag_Sigma).reshape(-1, 1)
                Sigma = np.diag(D) + np.dot(U, U.T)
                # Check the factor method agrees with reg. method
                S1 = smatrix.compute_smatrix(Sigma=None,
                                             D=D,
                                             U=U,
                                             method=method,
                                             how_approx='factor')
                S2 = smatrix.compute_smatrix(Sigma=Sigma,
                                             method=method,
                                             solver='cd',
                                             tol=0)
                np.testing.assert_array_almost_equal(
                    S1,
                    S2,
                    decimal=2,
                    err_msg=
                    f"factored/non-factored versions for method={method}, k={k} do not agree"
                )
Example #3
0
    def test_sdp_tolerance(self):

        # Get graph
        np.random.seed(110)
        Q = dgp.ErdosRenyi(p=50, tol=1e-1)
        V = utilities.cov2corr(utilities.chol2inv(Q))
        groups = np.concatenate([np.zeros(10) + j for j in range(5)]) + 1
        groups = groups.astype("int32")

        # Solve SDP
        for tol in [1e-3, 0.01, 0.02]:
            S = smatrix.compute_smatrix(
                Sigma=V,
                groups=groups,
                method="sdp",
                objective="pnorm",
                num_iter=10,
                tol=tol,
            )
            G = np.hstack([np.vstack([V, V - S]), np.vstack([V - S, V])])
            mineig = np.linalg.eig(G)[0].min()
            self.assertTrue(
                tol - mineig > -1 * tol / 10,
                f"sdp solver fails to control minimum eigenvalues: tol is {tol}, val is {mineig}",
            )
            self.check_S_properties(V, S, groups)
Example #4
0
    def test_blockdiag_approx_on_ar1(self):

        # Check that blockdiag approx yields a good soln on AR1
        p = 150
        a = 1
        b = 1
        # Create trivial and nontrivial groups
        triv_groups = np.arange(1, p + 1, 1)
        nontriv_groups = np.around(triv_groups / 2 + 0.01)
        nontriv_groups = utilities.preprocess_groups(nontriv_groups)
        # Sample data
        np.random.seed(110)
        dgprocess = dgp.DGP()
        _, _, _, _, V = dgprocess.sample_data(p=p, method="ar1", a=a, b=b)
        for groups in [triv_groups, nontriv_groups]:
            for method in ["mvr", "maxent", "mmi", "sdp"]:

                # Note we can't fit group mvr/maxent without pytorch
                nontriv_group_flag = np.all(groups == nontriv_groups)
                if not TORCH_AVAILABLE and nontriv_group_flag:
                    continue

                # Check that S_approx is valid
                S_approx = smatrix.compute_smatrix(V,
                                                   method=method,
                                                   groups=groups,
                                                   max_block=50)
                self.check_S_properties(V, S_approx, groups)

                # For exponentially decaying offdiags, should be quite similar
                # This seems to not be true for groups, so skip that for now...
                # For now, skip grouped mvr/mmi
                if nontriv_group_flag:
                    continue
                S_exact = smatrix.compute_smatrix(V,
                                                  method=method,
                                                  groups=groups)
                diff = np.abs(S_approx - S_exact)
                mean_diff = diff[S_exact != 0].mean()
                expected = 1e-2 if CHOLDATE_AVAILABLE else 5e-2
                identifier = f"for method={method}, groups={groups}"
                self.assertTrue(
                    mean_diff < expected,
                    msg=
                    f"Blockdiag apprx is {mean_diff} > {expected} on avg. away from exact soln {identifier}",
                )
Example #5
0
    def test_warnings_raised(self):

        print(
            f"torch={TORCH_AVAILABLE}, dsdp={DSDP_AVAILABLE}, choldate={CHOLDATE_AVAILABLE}"
        )

        # Sigma
        p = 50
        rho = 0.8
        Sigma = rho * np.ones((p, p)) + (1 - rho) * np.eye(p)

        for method, dependency_flag, dependency_name, suppress_kwargs in zip(
            ['mvr', 'maxent', 'sdp'],
            [CHOLDATE_AVAILABLE, CHOLDATE_AVAILABLE, DSDP_AVAILABLE],
            ["choldate", "choldate", "dsdp"], [{
                "choldate_warning": False
            }, {
                "choldate_warning": False
            }, {
                "dsdp_warning": False
            }]):
            # Check default setting
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always')
                smatrix.compute_smatrix(Sigma, method=method)
                if not dependency_flag:
                    self.assertTrue(dependency_name in str(w[-1].message))
                else:
                    self.assertTrue(len(w) == 0)
            # Check that it does not trigger with correct flag
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always')
                smatrix.compute_smatrix(Sigma,
                                        method=method,
                                        **suppress_kwargs)
                self.assertTrue(
                    len(w) == 0,
                    "warning still triggers with choldate_warning=False")
Example #6
0
    def test_cd_SDP(self):
        """
        Test that CD / SDP solvers get similar answers in a hard problem
        """

        np.random.seed(110)
        V = utilities.cov2corr(dgp.ErdosRenyi(p=100, tol=1e-1))
        S_cd = smatrix.compute_smatrix(Sigma=V,
                                       method="sdp",
                                       solver="cd",
                                       verbose=True,
                                       mu=0.95,
                                       num_iter=100)
        mac_cd = np.diag(S_cd).mean()
        S_sdp = smatrix.compute_smatrix(Sigma=V, method="sdp", solver="sdp")
        mac_sdp = np.diag(S_sdp).mean()
        np.testing.assert_almost_equal(
            mac_sdp,
            mac_cd,
            decimal=2,
            err_msg=
            f"compute_smatrix mac differs between SDP/CD solvers ({mac_sdp}, {mac_cd}, respectively"
        )
Example #7
0
    def test_maxent(self):
        """ Both maxent/mmi work properly """
        # Sample data
        dgprocess = dgp.DGP()
        dgprocess.sample_data(p=50, method='ar1', a=3)

        # Check solve_maxent/solve_mmi
        np.random.seed(110)
        S_ME = smatrix.compute_smatrix(dgprocess.Sigma, method='maxent')
        np.random.seed(110)
        S_MMI = smatrix.compute_smatrix(dgprocess.Sigma, method='mmi')
        np.testing.assert_array_almost_equal(
            S_ME,
            S_MMI,
            decimal=3,
            err_msg=f"compute_smatrix yields diff answers for mmi, maxent")

        # Check solve_maxent/solve_mmi
        np.random.seed(110)
        S_ME = mrc.solve_maxent(dgprocess.Sigma)
        np.random.seed(110)
        S_MMI = mrc.solve_mmi(dgprocess.Sigma)
        np.testing.assert_array_almost_equal(
            S_ME,
            S_MMI,
            decimal=3,
            err_msg=f"solve_maxent and solve_mmi yield different answers")

        # Check maxent_loss/mmi_loss
        L_ME = mrc.maxent_loss(dgprocess.Sigma, S_ME)
        L_MMI = mrc.mmi_loss(dgprocess.Sigma, S_MMI)
        np.testing.assert_almost_equal(
            L_ME,
            L_MMI,
            decimal=3,
            err_msg=f"maxent_loss and mmi_loss yield different answers")
Example #8
0
    def test_equicorr_SDP(self):

        # Test non-group SDP on equicorrelated cov matrix
        p = 100
        rhos = [0.6, 0.8, 0.9]
        solvers = ['sdp', 'cd']
        scales = [1, 5]
        for scale in scales:
            for rho in rhos:
                V = scale * rho * np.ones(
                    (p, p)) + scale * (1 - rho) * np.eye(p)
                for solver in solvers:
                    S = smatrix.compute_smatrix(Sigma=V,
                                                method="sdp",
                                                solver=solver,
                                                verbose=True)
                    expected = (2 - 2 * rho) * np.eye(p)
                    np.testing.assert_almost_equal(
                        S / scale,
                        expected,
                        decimal=2,
                        err_msg=
                        f"compute_smatrix produces incorrect S_SDP for equicorr, rho={rho}, scale={scale}, solver={solver}",
                    )
Example #9
0
    def test_easy_sdp(self):

        # Test non-group SDP first
        n = 200
        p = 50
        X, _, _, _, corr_matrix, groups = dgp.block_equi_graph(n=n,
                                                               p=p,
                                                               gamma=0.3)

        # S matrix
        trivial_groups = np.arange(0, p, 1) + 1
        S_triv = smatrix.compute_smatrix(
            Sigma=corr_matrix,
            groups=trivial_groups,
            method="sdp",
            verbose=True,
        )
        np.testing.assert_array_almost_equal(
            S_triv,
            np.eye(p),
            decimal=2,
            err_msg=
            "solve_group_SDP does not produce optimal S matrix (blockequi graphs)",
        )
        self.check_S_properties(corr_matrix, S_triv, trivial_groups)

        # Repeat for gaussian_knockoffs method
        ksampler = knockoffs.GaussianSampler(
            X=X,
            Sigma=corr_matrix,
            groups=trivial_groups,
            verbose=False,
            method="sdp",
        )
        S_triv2 = ksampler.fetch_S()

        np.testing.assert_array_almost_equal(
            S_triv2,
            np.eye(p),
            decimal=2,
            err_msg=
            "solve_group_SDP does not produce optimal S matrix (blockequi graphs)",
        )
        self.check_S_properties(corr_matrix, S_triv2, trivial_groups)

        # Test slightly harder case
        _, _, _, _, expected_out, _ = dgp.block_equi_graph(n=n, p=p, gamma=0)
        ksampler = knockoffs.GaussianSampler(X=X,
                                             Sigma=corr_matrix,
                                             groups=groups,
                                             verbose=False,
                                             method="sdp")
        S_harder = ksampler.fetch_S()
        np.testing.assert_almost_equal(
            S_harder,
            expected_out,
            decimal=2,
            err_msg=
            "solve_group_SDP does not produce optimal S matrix (blockequi graphs)",
        )
        self.check_S_properties(corr_matrix, S_harder, groups)

        # Repeat for ASDP
        ksampler = knockoffs.GaussianSampler(
            X=X,
            Sigma=corr_matrix,
            groups=groups,
            method="ASDP",
            verbose=False,
            max_block=10,
        )
        S_harder_ASDP = ksampler.fetch_S()
        np.testing.assert_almost_equal(
            S_harder_ASDP,
            expected_out,
            decimal=2,
            err_msg=
            "solve_group_ASDP does not produce optimal S matrix (blockequi graphs)",
        )
        self.check_S_properties(corr_matrix, S_harder_ASDP, groups)