예제 #1
0
class LatentDistanceGaussianWeightDistribution(GaussianWeightDistribution, GibbsSampling):
    """
    l_n ~ N(0, sigma^2 I)
    W_{n', n} ~ N(A * ||l_{n'} - l_{n}||_2^2 + b, ) for n' != n
    """

    def __init__(self, N, B=1, dim=2,
                 b=0.5,
                 sigma=None, Sigma_0=None, nu_0=None,
                 mu_self=0.0, eta=0.01):
        """
        Initialize SBM with parameters defined above.
        """
        super(LatentDistanceGaussianWeightDistribution, self).__init__(N)
        self.B = B
        self.dim = dim

        self.b = b
        self.eta = eta
        self.L = np.sqrt(eta) * np.random.randn(N,dim)

        if Sigma_0 is None:
            Sigma_0 = np.eye(B)

        if nu_0 is None:
            nu_0 = B + 2

        self.cov = GaussianFixedMean(mu=np.zeros(B), sigma=sigma, lmbda_0=Sigma_0, nu_0=nu_0)

        # Special case self-weights (along the diagonal)
        self._self_gaussian = Gaussian(mu_0=mu_self*np.ones(B),
                                       sigma_0=Sigma_0,
                                       nu_0=nu_0,
                                       kappa_0=1.0)

    @property
    def D(self):
        # return np.sqrt(((self.L[:, None, :] - self.L[None, :, :]) ** 2).sum(2))
        return ((self.L[:, None, :] - self.L[None, :, :]) ** 2).sum(2)

    @property
    def Mu(self):
        Mu = -self.D + self.b
        Mu = np.tile(Mu[:,:,None], (1,1,self.B))
        for n in xrange(self.N):
            Mu[n,n,:] = self._self_gaussian.mu

        return Mu

    @property
    def Sigma(self):
        sig = self.cov.sigma
        Sig = np.tile(sig[None,None,:,:], (self.N, self.N,1,1))

        for n in xrange(self.N):
            Sig[n,n,:,:] = self._self_gaussian.sigma

        return Sig

    def initialize_from_prior(self):
        self.L = np.sqrt(self.eta) * np.random.randn(self.N, self.dim)
        self.cov.resample()

    def initialize_hypers(self, W):
        # Optimize the initial locations
        self._optimize_L(np.ones((self.N,self.N)), W)

    def log_prior(self):
        """
        Compute the prior probability of F, mu0, and lmbda
        """
        from graphistician.internals.utils import \
            normal_inverse_wishart_log_prob, \
            inverse_wishart_log_prob
        lp = 0

        # Log prior of F under spherical Gaussian prior
        from scipy.stats import norm, invgamma
        lp += invgamma.logpdf(self.eta, 1, 1)
        lp += norm.logpdf(self.b, 0, 1)
        lp += norm.logpdf(self.L, 0, 1).sum()

        lp += inverse_wishart_log_prob(self.cov)
        lp += normal_inverse_wishart_log_prob(self._self_gaussian)

        # Log prior of mu_0 and mu_self
        return lp

    def _hmc_log_probability(self, L, b, A, W):
        """
        Compute the log probability as a function of L.
        This allows us to take the gradients wrt L using autograd.
        :param L:
        :param A:
        :return:
        """
        assert self.B == 1
        import autograd.numpy as anp

        # Compute pairwise distance
        L1 = anp.reshape(L,(self.N,1,self.dim))
        L2 = anp.reshape(L,(1,self.N,self.dim))
        # Mu = a * anp.sqrt(anp.sum((L1-L2)**2, axis=2)) + b
        Mu = -anp.sum((L1-L2)**2, axis=2) + b

        Aoff = A * (1-anp.eye(self.N))
        X = (W - Mu[:,:,None]) * Aoff[:,:,None]

        # Get the covariance and precision
        Sig = self.cov.sigma[0,0]
        Lmb = 1./Sig

        lp = anp.sum(-0.5 * X**2 * Lmb)

        # Log prior of L under spherical Gaussian prior
        lp += -0.5 * anp.sum(L * L / self.eta)

        # Log prior of mu0 under standardGaussian prior
        lp += -0.5 * b ** 2

        return lp

    def sample_predictive_parameters(self):
        Lext = \
            np.vstack((self.L, np.sqrt(self.eta) * np.random.randn(1, self.dim)))

        # Compute mean and covariance over extended space
        D = ((Lext[:,None,:] - Lext[None,:,:])**2).sum(2)
        Mu = -D + self.b
        Mu_row = np.tile(Mu[-1,:][:,None], (1,self.B))
        Mu_row[-1] = self._self_gaussian.mu
        Mu_col = Mu_row.copy()

        # Mu = np.tile(Mu[:,:,None], (1,1,self.B))
        # for n in xrange(self.N+1):
        #     Mu[n,n,:] = self._self_gaussian.mu

        L = np.linalg.cholesky(self.cov.sigma)
        L_row = np.tile(L[None,:,:], (self.N+1, 1, 1))
        L_row[-1] = np.linalg.cholesky(self._self_gaussian.sigma)
        L_col = L_row.copy()

        # L = np.tile(L[None,None,:,:], (self.N+1, self.N+1, 1, 1))
        # for n in xrange(self.N+1):
        #     L[n,n,:,:] = np.linalg.cholesky(self._self_gaussian.sigma)

        # Mu_row, Mu_col = Mu[-1,:,:], Mu[:,-1,:]
        # L_row, L_col = L[-1,:,:,:], L[:,-1,:,:]
        return Mu_row, Mu_col, L_row, L_col


    def resample(self, (A,W)):
        self._resample_L(A, W)
        self._resample_b(A, W)
        self._resample_cov(A, W)
        self._resample_self_gaussian(A, W)
        self._resample_eta()
예제 #2
0
class SBMGaussianWeightSharedCov(SBMGaussianWeightDistribution):
    def __init__(self, N, B=1,
                 C=3, pi=10.0,
                 mu_0=None, Sigma_0=None, nu_0=None,
                 special_case_self_conns=True):

        super(SBMGaussianWeightSharedCov, self).\
            __init__(N, B=B, C=C, pi=pi,
                     mu_0=mu_0, Sigma_0=Sigma_0, nu_0=nu_0,
                     special_case_self_conns=special_case_self_conns)

        if mu_0 is None:
            mu_0 = np.zeros(B)

        if Sigma_0 is None:
            Sigma_0 = np.eye(B)

        if nu_0 is None:
            nu_0 = B + 2

        self._cov_model = GaussianFixedMean(mu=np.zeros(B),
                                            nu_0=nu_0, lmbda_0=Sigma_0)

        self._gaussians = [[GaussianFixedCov(mu_0=mu_0, sigma_0=np.eye(B),
                                             sigma=self._cov_model.sigma)
                            for _ in xrange(C)]
                           for _ in xrange(C)]

    def resample_mu_and_Sig(self, A, W):
        """
        Resample p given observations of the weights
        """
        Abool = A.astype(np.bool)

        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                mask = self._get_mask(Abool, c1, c2)
                self._gaussians[c1][c2].resample(W[mask])

        # Resample self connection
        if self.special_case_self_conns:
            mask = np.eye(self.N, dtype=np.bool) & Abool
            self._self_gaussian.resample(W[mask])

        # Resample covariance
        A_offdiag = Abool.copy()
        np.fill_diagonal(A_offdiag, False)
        W_cent = (W - self.Mu)[A_offdiag]
        self._cov_model.resample(W_cent)

        # Update gaussians
        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                self._gaussians[c1][c2].sigma = self._cov_model.sigma

    def log_prior(self):
        """
        Compute the log likelihood of a set of SBM parameters

        :param x:    (m,p,v) tuple
        :return:
        """
        from scipy.stats import dirichlet
        lp = 0

        # Get the log probability of the block probabilities
        lp += dirichlet(self.pi).logpdf(self.m)

        # Get the prior probability of the Gaussian parameters under NIW prior
        # for c1 in xrange(self.C):
        #     for c2 in xrange(self.C):
        #         lp += normal_inverse_wishart_log_prob(self._gaussians[c1][c2])
        #
        # if self.special_case_self_conns:
        #     lp += normal_inverse_wishart_log_prob(self._self_gaussian)

        # Get the probability of the block assignments
        lp += (np.log(self.m)[self.c]).sum()
        return lp


    def initialize_hypers(self, W):
        mu_0 = W.mean(axis=(0,1))
        sigma_0 = np.diag(W.var(axis=(0,1)))

        # Set the global cov
        nu_0 = self._cov_model.nu_0
        self._cov_model.sigma_0 = sigma_0 * (nu_0 - self.B - 1)

        # Set the mean
        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                self._gaussians[c1][c2].mu_0 = mu_0
                self._gaussians[c1][c2].sigma = self._cov_model.sigma_0
                self._gaussians[c1][c2].resample()

        if self.special_case_self_conns:
            W_self = W[np.arange(self.N), np.arange(self.N)]
            self._self_gaussian.mu_0 = W_self.mean(axis=0)
            self._self_gaussian.sigma_0 = np.diag(W_self.var(axis=0))
            self._self_gaussian.resample()

        # Cluster the neurons based on their rows and columns
        from sklearn.cluster import KMeans
        features = np.hstack((W[:,:,0], W[:,:,0].T))
        km = KMeans(n_clusters=self.C)
        km.fit(features)
        self.c = km.labels_.astype(np.int)

        print "Initial c: ", self.c
예제 #3
0
class SBMGaussianWeightSharedCov(SBMGaussianWeightDistribution):
    def __init__(self,
                 N,
                 B=1,
                 C=3,
                 pi=10.0,
                 mu_0=None,
                 Sigma_0=None,
                 nu_0=None,
                 special_case_self_conns=True):

        super(SBMGaussianWeightSharedCov, self).\
            __init__(N, B=B, C=C, pi=pi,
                     mu_0=mu_0, Sigma_0=Sigma_0, nu_0=nu_0,
                     special_case_self_conns=special_case_self_conns)

        if mu_0 is None:
            mu_0 = np.zeros(B)

        if Sigma_0 is None:
            Sigma_0 = np.eye(B)

        if nu_0 is None:
            nu_0 = B + 2

        self._cov_model = GaussianFixedMean(mu=np.zeros(B),
                                            nu_0=nu_0,
                                            lmbda_0=Sigma_0)

        self._gaussians = [[
            GaussianFixedCov(mu_0=mu_0,
                             sigma_0=np.eye(B),
                             sigma=self._cov_model.sigma) for _ in xrange(C)
        ] for _ in xrange(C)]

    def resample_mu_and_Sig(self, A, W):
        """
        Resample p given observations of the weights
        """
        Abool = A.astype(np.bool)

        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                mask = self._get_mask(Abool, c1, c2)
                self._gaussians[c1][c2].resample(W[mask])

        # Resample self connection
        if self.special_case_self_conns:
            mask = np.eye(self.N, dtype=np.bool) & Abool
            self._self_gaussian.resample(W[mask])

        # Resample covariance
        A_offdiag = Abool.copy()
        np.fill_diagonal(A_offdiag, False)
        W_cent = (W - self.Mu)[A_offdiag]
        self._cov_model.resample(W_cent)

        # Update gaussians
        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                self._gaussians[c1][c2].sigma = self._cov_model.sigma

    def log_prior(self):
        """
        Compute the log likelihood of a set of SBM parameters

        :param x:    (m,p,v) tuple
        :return:
        """
        from scipy.stats import dirichlet
        lp = 0

        # Get the log probability of the block probabilities
        lp += dirichlet(self.pi).logpdf(self.m)

        # Get the prior probability of the Gaussian parameters under NIW prior
        # for c1 in xrange(self.C):
        #     for c2 in xrange(self.C):
        #         lp += normal_inverse_wishart_log_prob(self._gaussians[c1][c2])
        #
        # if self.special_case_self_conns:
        #     lp += normal_inverse_wishart_log_prob(self._self_gaussian)

        # Get the probability of the block assignments
        lp += (np.log(self.m)[self.c]).sum()
        return lp

    def initialize_hypers(self, W):
        mu_0 = W.mean(axis=(0, 1))
        sigma_0 = np.diag(W.var(axis=(0, 1)))

        # Set the global cov
        nu_0 = self._cov_model.nu_0
        self._cov_model.sigma_0 = sigma_0 * (nu_0 - self.B - 1)

        # Set the mean
        for c1 in xrange(self.C):
            for c2 in xrange(self.C):
                self._gaussians[c1][c2].mu_0 = mu_0
                self._gaussians[c1][c2].sigma = self._cov_model.sigma_0
                self._gaussians[c1][c2].resample()

        if self.special_case_self_conns:
            W_self = W[np.arange(self.N), np.arange(self.N)]
            self._self_gaussian.mu_0 = W_self.mean(axis=0)
            self._self_gaussian.sigma_0 = np.diag(W_self.var(axis=0))
            self._self_gaussian.resample()

        # Cluster the neurons based on their rows and columns
        from sklearn.cluster import KMeans
        features = np.hstack((W[:, :, 0], W[:, :, 0].T))
        km = KMeans(n_clusters=self.C)
        km.fit(features)
        self.c = km.labels_.astype(np.int)

        print "Initial c: ", self.c
예제 #4
0
class LatentDistanceGaussianWeightDistribution(GaussianWeightDistribution,
                                               GibbsSampling):
    """
    l_n ~ N(0, sigma^2 I)
    W_{n', n} ~ N(A * ||l_{n'} - l_{n}||_2^2 + b, ) for n' != n
    """
    def __init__(self,
                 N,
                 B=1,
                 dim=2,
                 b=0.5,
                 sigma=None,
                 Sigma_0=None,
                 nu_0=None,
                 mu_self=0.0,
                 eta=0.01):
        """
        Initialize SBM with parameters defined above.
        """
        super(LatentDistanceGaussianWeightDistribution, self).__init__(N)
        self.B = B
        self.dim = dim

        self.b = b
        self.eta = eta
        self.L = np.sqrt(eta) * np.random.randn(N, dim)

        if Sigma_0 is None:
            Sigma_0 = np.eye(B)

        if nu_0 is None:
            nu_0 = B + 2

        self.cov = GaussianFixedMean(mu=np.zeros(B),
                                     sigma=sigma,
                                     lmbda_0=Sigma_0,
                                     nu_0=nu_0)

        # Special case self-weights (along the diagonal)
        self._self_gaussian = Gaussian(mu_0=mu_self * np.ones(B),
                                       sigma_0=Sigma_0,
                                       nu_0=nu_0,
                                       kappa_0=1.0)

    @property
    def D(self):
        # return np.sqrt(((self.L[:, None, :] - self.L[None, :, :]) ** 2).sum(2))
        return ((self.L[:, None, :] - self.L[None, :, :])**2).sum(2)

    @property
    def Mu(self):
        Mu = -self.D + self.b
        Mu = np.tile(Mu[:, :, None], (1, 1, self.B))
        for n in xrange(self.N):
            Mu[n, n, :] = self._self_gaussian.mu

        return Mu

    @property
    def Sigma(self):
        sig = self.cov.sigma
        Sig = np.tile(sig[None, None, :, :], (self.N, self.N, 1, 1))

        for n in xrange(self.N):
            Sig[n, n, :, :] = self._self_gaussian.sigma

        return Sig

    def initialize_from_prior(self):
        self.L = np.sqrt(self.eta) * np.random.randn(self.N, self.dim)
        self.cov.resample()

    def initialize_hypers(self, W):
        # Optimize the initial locations
        self._optimize_L(np.ones((self.N, self.N)), W)

    def log_prior(self):
        """
        Compute the prior probability of F, mu0, and lmbda
        """
        from graphistician.internals.utils import \
            normal_inverse_wishart_log_prob, \
            inverse_wishart_log_prob
        lp = 0

        # Log prior of F under spherical Gaussian prior
        from scipy.stats import norm, invgamma
        lp += invgamma.logpdf(self.eta, 1, 1)
        lp += norm.logpdf(self.b, 0, 1)
        lp += norm.logpdf(self.L, 0, 1).sum()

        lp += inverse_wishart_log_prob(self.cov)
        lp += normal_inverse_wishart_log_prob(self._self_gaussian)

        # Log prior of mu_0 and mu_self
        return lp

    def _hmc_log_probability(self, L, b, A, W):
        """
        Compute the log probability as a function of L.
        This allows us to take the gradients wrt L using autograd.
        :param L:
        :param A:
        :return:
        """
        assert self.B == 1
        import autograd.numpy as anp

        # Compute pairwise distance
        L1 = anp.reshape(L, (self.N, 1, self.dim))
        L2 = anp.reshape(L, (1, self.N, self.dim))
        # Mu = a * anp.sqrt(anp.sum((L1-L2)**2, axis=2)) + b
        Mu = -anp.sum((L1 - L2)**2, axis=2) + b

        Aoff = A * (1 - anp.eye(self.N))
        X = (W - Mu[:, :, None]) * Aoff[:, :, None]

        # Get the covariance and precision
        Sig = self.cov.sigma[0, 0]
        Lmb = 1. / Sig

        lp = anp.sum(-0.5 * X**2 * Lmb)

        # Log prior of L under spherical Gaussian prior
        lp += -0.5 * anp.sum(L * L / self.eta)

        # Log prior of mu0 under standardGaussian prior
        lp += -0.5 * b**2

        return lp

    def sample_predictive_parameters(self):
        Lext = \
            np.vstack((self.L, np.sqrt(self.eta) * np.random.randn(1, self.dim)))

        # Compute mean and covariance over extended space
        D = ((Lext[:, None, :] - Lext[None, :, :])**2).sum(2)
        Mu = -D + self.b
        Mu_row = np.tile(Mu[-1, :][:, None], (1, self.B))
        Mu_row[-1] = self._self_gaussian.mu
        Mu_col = Mu_row.copy()

        # Mu = np.tile(Mu[:,:,None], (1,1,self.B))
        # for n in xrange(self.N+1):
        #     Mu[n,n,:] = self._self_gaussian.mu

        L = np.linalg.cholesky(self.cov.sigma)
        L_row = np.tile(L[None, :, :], (self.N + 1, 1, 1))
        L_row[-1] = np.linalg.cholesky(self._self_gaussian.sigma)
        L_col = L_row.copy()

        # L = np.tile(L[None,None,:,:], (self.N+1, self.N+1, 1, 1))
        # for n in xrange(self.N+1):
        #     L[n,n,:,:] = np.linalg.cholesky(self._self_gaussian.sigma)

        # Mu_row, Mu_col = Mu[-1,:,:], Mu[:,-1,:]
        # L_row, L_col = L[-1,:,:,:], L[:,-1,:,:]
        return Mu_row, Mu_col, L_row, L_col

    def resample(self, (A, W)):
        self._resample_L(A, W)
        self._resample_b(A, W)
        self._resample_cov(A, W)
        self._resample_self_gaussian(A, W)
        self._resample_eta()
예제 #5
0
class _LatentDistanceModelGaussianMixin(_NetworkModel):
    """
    l_n ~ N(0, sigma^2 I)
    W_{n', n} ~ N(A * ||l_{n'} - l_{n}||_2^2 + b, ) for n' != n
    """
    def __init__(self,
                 N,
                 B=1,
                 dim=2,
                 b=0.5,
                 sigma=None,
                 Sigma_0=None,
                 nu_0=None,
                 mu_self=0.0,
                 eta=0.01):

        super(_LatentDistanceModelGaussianMixin, self).__init__(N, B)
        self.B = B
        self.dim = dim

        self.b = b
        self.eta = eta
        self.L = np.sqrt(eta) * np.random.randn(N, dim)

        if Sigma_0 is None:
            Sigma_0 = np.eye(B)

        if nu_0 is None:
            nu_0 = B + 2

        self.cov = GaussianFixedMean(mu=np.zeros(B),
                                     sigma=sigma,
                                     lmbda_0=Sigma_0,
                                     nu_0=nu_0)

        # Special case self-weights (along the diagonal)
        self._self_gaussian = Gaussian(mu_0=mu_self * np.ones(B),
                                       sigma_0=Sigma_0,
                                       nu_0=nu_0,
                                       kappa_0=1.0)

    @property
    def D(self):
        # return np.sqrt(((self.L[:, None, :] - self.L[None, :, :]) ** 2).sum(2))
        return ((self.L[:, None, :] - self.L[None, :, :])**2).sum(2)

    @property
    def mu_W(self):
        Mu = -self.D + self.b
        Mu = np.tile(Mu[:, :, None], (1, 1, self.B))
        for n in range(self.N):
            Mu[n, n, :] = self._self_gaussian.mu

        return Mu

    @property
    def sigma_W(self):
        sig = self.cov.sigma
        Sig = np.tile(sig[None, None, :, :], (self.N, self.N, 1, 1))

        for n in range(self.N):
            Sig[n, n, :, :] = self._self_gaussian.sigma

        return Sig

    def initialize_from_prior(self):
        self.L = np.sqrt(self.eta) * np.random.randn(self.N, self.dim)
        self.cov.resample()

    def initialize_hypers(self, W):
        # Optimize the initial locations
        self._optimize_L(np.ones((self.N, self.N)), W)

    def _hmc_log_probability(self, L, b, A, W):
        """
        Compute the log probability as a function of L.
        This allows us to take the gradients wrt L using autograd.
        :param L:
        :param A:
        :return:
        """
        assert self.B == 1
        import autograd.numpy as atnp

        # Compute pairwise distance
        L1 = atnp.reshape(L, (self.N, 1, self.dim))
        L2 = atnp.reshape(L, (1, self.N, self.dim))
        # Mu = a * anp.sqrt(anp.sum((L1-L2)**2, axis=2)) + b
        Mu = -atnp.sum((L1 - L2)**2, axis=2) + b

        Aoff = A * (1 - atnp.eye(self.N))
        X = (W - Mu[:, :, None]) * Aoff[:, :, None]

        # Get the covariance and precision
        Sig = self.cov.sigma[0, 0]
        Lmb = 1. / Sig

        lp = atnp.sum(-0.5 * X**2 * Lmb)

        # Log prior of L under spherical Gaussian prior
        lp += -0.5 * atnp.sum(L * L / self.eta)

        # Log prior of mu0 under standardGaussian prior
        lp += -0.5 * b**2

        return lp

    def resample(self, data=[]):
        super(_LatentDistanceModelGaussianMixin, self).resample(data)
        A, W = data
        N, B = self.N, self.B
        self._resample_L(A, W)
        self._resample_b(A, W)
        self._resample_cov(A, W)
        self._resample_self_gaussian(A, W)
        self._resample_eta()
        # print "eta: ", self.eta, "\tb: ", self.b

    def _resample_L(self, A, W):
        """
        Resample the locations given A
        :return:
        """
        from autograd import grad
        from hips.inference.hmc import hmc

        lp = lambda L: self._hmc_log_probability(L, self.b, A, W)
        dlp = grad(lp)

        stepsz = 0.005
        nsteps = 10
        # lp0 = lp(self.L)
        self.L = hmc(lp,
                     dlp,
                     stepsz,
                     nsteps,
                     self.L.copy(),
                     negative_log_prob=False)
        # lpf = lp(self.L)
        # print "diff lp: ", (lpf - lp0)

    def _optimize_L(self, A, W):
        """
        Resample the locations given A
        :return:
        """
        import autograd.numpy as atnp
        from autograd import grad
        from scipy.optimize import minimize

        lp = lambda Lflat: -self._hmc_log_probability(
            atnp.reshape(Lflat, (self.N, 2)), self.b, A, W)
        dlp = grad(lp)

        res = minimize(lp, np.ravel(self.L), jac=dlp, method="bfgs")

        self.L = np.reshape(res.x, (self.N, 2))

    def _resample_b_hmc(self, A, W):
        """
        Resample the distance dependence offset
        :return:
        """
        # TODO: We could sample from the exact Gaussian conditional
        from autograd import grad
        from hips.inference.hmc import hmc

        lp = lambda b: self._hmc_log_probability(self.L, b, A, W)
        dlp = grad(lp)

        stepsz = 0.0001
        nsteps = 10
        b = hmc(lp,
                dlp,
                stepsz,
                nsteps,
                np.array(self.b),
                negative_log_prob=False)
        self.b = float(b)
        print("b: ", self.b)

    def _resample_b(self, A, W):
        """
        Resample the distance dependence offset
        W ~ N(mu, sigma)
          = N(-D + b, sigma)
    
        implies
        W + D ~ N(b, sigma).
    
        If b ~ N(0, 1), we can compute the Gaussian conditional
        in closed form.
        """
        D = self.D
        sigma = self.cov.sigma[0, 0]
        Aoff = (A * (1 - np.eye(self.N))).astype(np.bool)
        X = (W + D[:, :, None])[Aoff]

        # Now X ~ N(b, sigma)
        mu0, sigma0 = 0.0, 1.0
        N = X.size
        sigma_post = 1. / (1. / sigma0 + N / sigma)
        mu_post = sigma_post * (mu0 / sigma0 + X.sum() / sigma)

        self.b = mu_post + np.sqrt(sigma_post) * np.random.randn()
        # print "b: ", self.b

    def _resample_cov(self, A, W):
        # Resample covariance matrix
        Mu = self.Mu
        mask = (True - np.eye(self.N, dtype=np.bool)) & A.astype(np.bool)
        self.cov.resample(W[mask] - Mu[mask])

    def _resample_self_gaussian(self, A, W):
        # Resample self connection
        mask = np.eye(self.N, dtype=np.bool) & A.astype(np.bool)
        self._self_gaussian.resample(W[mask])

    def _resample_eta(self):
        """
        Resample sigma under an inverse gamma prior, sigma ~ IG(1,1)
        :return:
        """
        L = self.L

        a_prior = 1.0
        b_prior = 1.0

        a_post = a_prior + L.size / 2.0
        b_post = b_prior + (L**2).sum() / 2.0

        from scipy.stats import invgamma
        self.eta = invgamma.rvs(a=a_post, scale=b_post)