Example #1
0
def gammaPosteriorPrecIsoSd(data, 
                    means, labelList, 
                    gammaShape, gammaRate, scale):
    """
    Inputs:
        data: (N,D) array
        means: (K,) list of (D,) arrays
        labelList: (N,) list
        gammaShape, gammaRate:
        scale: scalar

    Outputs:
        precShape: scalar
        precRate: scalar
    """
    
    z, _ = partitions.labelHelpersToLabelsAndClusts(labelList)
    N, D = data.shape

    nClust = len(means)
    squaredNorm = np.square(means) # (K,D)
    squaredNormTotal = np.sum(squaredNorm)

    precShape = gammaShape + (nClust*D)/2
    precRate = gammaRate + squaredNormTotal/2

    squaredErr = np.square(data - np.array(means)[z]) # (N,D)
    squaredErrTotal = np.sum(squaredErr)

    precShape += (N*D)/2
    precRate += scale*squaredErrTotal/2

    return precShape, precRate
Example #2
0
def gammaPosteriorPrec(data, 
                    means, labelList, 
                    gammaShape, gammaRate, scale):
    """
    Inputs:
        data: (N,D) array
        means: (K,) list of (D,) arrays
        labelList: (N,) list
        gammaShape, gammaRate:
        scale: scalar

    Outputs:
        precShape: scalar
        precRate: (D,) array
    """
    
    z, _ = partitions.labelHelpersToLabelsAndClusts(labelList)
    N = data.shape[0]

    nClust = len(means)
    squaredNorm = np.square(means)
    squaredNormPerDim = np.sum(squaredNorm, axis=0) # (D,)
    precShape = gammaShape + nClust/2
    precRate = gammaRate + squaredNormPerDim/2

    squaredErr = np.square(data - np.array(means)[z])
    squaredErrPerDim = np.sum(squaredErr, axis=0) # (D,)
    precShape += N/2
    precRate += scale*squaredErrPerDim/2

    return precShape, precRate
Example #3
0
    def sample(self, i, j):
        """
        Inputs:
            i, j: scalars (or None), indices of data to localize clusters for split/merge
                if None, decide i, j in this call
        
        Output:
            Propose either a split or a merge and decide if we switch to that state. 

        """
        z, clusts = partitions.labelHelpersToLabelsAndClusts(self.labelList)

        # make launch state
        if (i is None) or (j is None):
            i, j, S = self.makeIJS(z, clusts)
        # print("clusts ", clusts)
        # print("i ", i, "j ", j)
        # print("S ", S)
        else:
            S_as_set = (clusts[z[i]] | clusts[z[j]]) - set([i,j])
            # convert S to list to ensure iteration order is consistent
            S = list(S_as_set)

        launchLabelList = self.makeLaunchState(z, i, j, S, self.splitMergeDict["numRes"])
        # print("z_launch ", z_launch)

        # make split proposal
        if (z[i] == z[j]):
            # print("Proposing split")
            nextLabelList, nextClusts = self.proposeRestrictedGibbs(launchLabelList, i, j, S, 1)
        # make merge proposal
        else:
            # print("Proposing merge")
            # probably don't actually need to get the merged state to compute the 
            # acceptance probability
            nextLabelList, _, nextClusts = partitions.reassignClust(z, clusts, z[i], z[j])
        # print("Proposed state is z_next ", z_next)

        logAcc = self.acceptanceProbability(self.labelList, self.clusts,
                                                launchLabelList,
                                                nextLabelList, nextClusts,
                                                i, j, S)
        # print("log acc is ", logAcc)

        # accept or reject
        if (np.log(self.rng.uniform()) < logAcc):
            # print("Accept proposal")
            accept = 1
            labelList = nextLabelList
            clusts = nextClusts
        else:
            # print("Reject proposal")
            accept = 0
            labelList = self.labelList
            clusts = self.clusts

        return accept, labelList, clusts
Example #4
0
    def proposeRestrictedGibbs(self, labelList, i, j, S, numRes):
        """
        Args:
            labelList: (N,) list, each is a LabelHelper object
            i, j: scalars, indices selected for split-merge
            S: list,
            numRes: scalar, how many restricted sweeps to do

        Output: 
            nextLabelList: (N,) list
            nextClusts: (K,) list of sets, each set is the indices of data in one
                cluster

        Remark:
            this function needs to coordinate with proposal_prob in terms of the 
            ordering of the indices S, since the random MH proposal is determined
            by both the launch state but also the ordering of S which governs the 
            restricted Gibbs.
        """

        z, _ = partitions.labelHelpersToLabelsAndClusts(labelList)
        assert z[i] != z[j]

        # make copy of start state 
        nextLabelList = partitions.zToLabelHelpers(z)
        nextClusts = partitions.zToClusts(z)

        for it in range(numRes):
            for idx in S:
                # remove idx from clusts. Will not change the labels of any clusters
                # (which only happens if the popped idx were the only member of its cluster)
                # since we know that the cluster of i and j will always have at least
                # i or j
                removedClust = partitions.pop_idx(nextLabelList, nextClusts, 
                                                    None, None, 
                                                    idx, None)
                assert removedClust is None

                # z, clusts, _ = partitions.pop_idx(z, clusts, idx)
                probs, _ = self.restrictedClusterWeights(nextLabelList, nextClusts, 
                                                            idx, i, j)
                # print("it = %d" %it, "probs: ", probs)
                clust_idx = self.rng.choice(len(probs), p=probs)
                # update clusts and assignments
                if (clust_idx == 0):
                    newC = nextLabelList[i].getValue()
                else:
                    assert (clust_idx == 1)
                    newC = nextLabelList[j].getValue()
                
                labelObj = partitions.labelOfCluster(nextLabelList, nextClusts, newC)
                nextLabelList[idx] = labelObj
                nextClusts[newC].add(idx)
        return nextLabelList, nextClusts
def CC(labelList, indices):
    """
    Input:
        z: (N,) vector of labels
        indices: (M,) indices of data to check co-cluster.
    Output:
        mat: (M^2,) flattened matrix whether two observations are in the same cluster
    Remark:
        we avoid reporting and saving the whole (N,N) co-clustering matrix for memory reasons 
    """
    z, clusts = partitions.labelHelpersToLabelsAndClusts(labelList)
    mat = partitions.adj_matrix(z, clusts)
    sub_mat = mat[np.ix_(indices, indices)]  # (M,M)
    res = sub_mat.flatten()  # (M^2,)
    return res
Example #6
0
def greedyColoring(g, nColors):
    """
    Greedy coloring of g using nColors using the 0,1,2,... ordering of vertices. 
    If coloring is possible, return coloring. 

    Return
        possible: boolean
        labelList: list of LabelHelper objects
        clusts: (nColors,) list of sets
    """
    nvertices = g.vcount()
    vertexColors = -np.ones([nvertices], dtype=int)
    color_ids = set() 
    for ivertex in range(nvertices):
        n_i = igraph.Graph.neighbors(g, ivertex)
        legal_colors = color_ids.difference(vertexColors[n_i])
        if len(legal_colors) == 0:
            new_color_id = len(color_ids)
            color_ids.add(new_color_id)
            legal_colors.add(new_color_id)
        vertexColors[ivertex] = min(legal_colors)

    nColorsUsed = len(set(vertexColors))
    if (nColorsUsed <= nColors):
        possible = True
        labelList = [0 for _ in range(nvertices)]
        for label in range(nColorsUsed):
            labelObj = partitions.LabelHelper(label)
            equalIndices = np.argwhere(vertexColors == label)[:,0]
            for idx in equalIndices:
                labelList[idx] = labelObj
        _ , clusts = partitions.labelHelpersToLabelsAndClusts(labelList)
    else:
        possible = False
        labelList, clusts = None, None
    return possible, labelList, clusts
Example #7
0
    def proposalProbability(self,  
                            startLabelList, 
                            endLabelList,
                            i, j, S):
        """
        Compute the probability of transitioning from z_start to z_end using 
        restricted_Gibbs. There is only one way of taking restricted_Gibbs move
        to end at z_split when beginning from z_launch, if the list S has been fixed.

        Inputs:
            startLabelList:
            endLabelList:
            i, j, S:

        Outputs:
            cumu_log_prob: scalar

        Remark:
            Jain and Neal 2004 Eq 3.14 doesn't explicitly say what 
            the ordering of the indices in S is. Wallach's code seemingly uses a for 
            comprehension for S (line 59 in conjugate_split_merge.py), but it looks like 
            they compute the z_split and the proposal in the same loop, so that ensures 
            the same ordering of the indices in S.
        """

        startZ, _ = partitions.labelHelpersToLabelsAndClusts(startLabelList)

        assert startZ[i] != startZ[j]

        # print("startZ", startZ, "z_end", z_end)

        # make copy of start state 
        startLabelListCopy = partitions.zToLabelHelpers(startZ)
        startClustsCopy = partitions.zToClusts(startZ)

        cumu_log_prob = 0.0

        for idx in S:
            # print("z, clusts before pop", z, clusts)
            # z, clusts, _ = partitions.pop_idx(z, clusts, idx)
            removedClust =  partitions.pop_idx(startLabelListCopy, startClustsCopy, None, None, idx, None)
            assert removedClust is None
            # print("clusts after pop", clusts)
            _, log_probs = self.restrictedClusterWeights(startLabelListCopy, startClustsCopy, 
                                                        idx, i, j)
            final_val = endLabelList[idx].getValue()
            # print("final_val ",final_val)
            if (final_val == endLabelList[i].getValue()):
                cumu_log_prob += log_probs[0]
            else:
                assert final_val == endLabelList[j].getValue()
                cumu_log_prob += log_probs[1]

            # if (final_val == len(startClustsCopy)):
            #     startClustsCopy.append(set())
            #     labelObj = partitions.LabelHelper(final_val)
            # else:
            #     labelObj = partitions.labelOfCluster(startLabelListCopy, startClustsCopy, final_val)

            labelObj = partitions.labelOfCluster(startLabelListCopy, startClustsCopy, final_val)
            startLabelListCopy[idx] = labelObj
            startClustsCopy[final_val].add(idx)
            # print("idx ", idx, "cumu_log_prob after idx", cumu_log_prob)

        return cumu_log_prob
Example #8
0
def initialPartitionAndMeans(data, initZ, alpha, initType, rng):
    """initialPartitionAndMeans() initializes the Markov chain over DPMM 
    clustering models.

    Args:
        data: (N,D) array, observations
        initZ: (N,) array 
            if None, use initType to set initial state
        alpha: DPMM concentration
        initType: str, how to generate first sample
        rng: bitGenerator

    Output:
        labelList: (N,) list, each is a labelHelper object
        clusts: (K,) list of sets
        clustSums: (K,) list of (D,) array
        means: (K,) list of (D,) array
    """
    Ndata = data.shape[0]

    if initZ is None:
        if initType == "allSame":
            z0 = np.zeros(Ndata, dtype=np.int)
        elif initType == "crpPrior":
            z0 = crp_prior(Ndata, alpha, rng)
        else:
            typ, k_size = initType.split("=")
            assert typ == "kmeans"
            z0 = kmeans_init(data, alpha, int(k_size))
    else:
        z0 = initZ.copy()

    nLabels = len(np.unique(z0))
    labelList = [0 for _ in range(Ndata)]
    clustSums, means = [0 for _ in range(nLabels)], [0 for _ in range(nLabels)]
    for label in range(nLabels):
        labelObj = partitions.LabelHelper(label)
        equalIndices = np.argwhere(z0 == label)[:,0]
        for idx in equalIndices:
            labelList[idx] = labelObj
        clustSums[label] = np.sum(data[equalIndices,:], axis=0)
        means[label] = np.mean(data[equalIndices,:], axis=0)
    _, clusts = partitions.labelHelpersToLabelsAndClusts(labelList)

    return labelList, clusts, clustSums, means

# def init_swap(single_transition, pi0):    
#     """
#     Jointly initialize the X and Y chains to induce anti-correlated correction terms.
    
#     Args:
#         single_transition: lambda function, marginal Gibbs sweep, typically gibbs_sweep_single()
#             from sampler_single.py
#         pi0: lambda function, initial distribution --- each call generates a sample
#     """
    
#     a0, b0 = pi0(), pi0()
    
#     a1 = single_transition(a0)
#     b1 = single_transition(b0)
#     b2 = single_transition(b1)
    
#     X1,X2 = [a0,a1], [b0]
#     Y1,Y2 = [b1,b2], [a1]
    
#     return X1,X2,Y1,Y2

## ----------------------------------------------------------------------
## track means
# def init_centers(z, D, sd0, rng):
#     """
#     Draw cluster means from the prior distribution.
#     """
#     nClusts = max(z)+1
#     mu0 = np.zeros(D)
#     centers = [rng.multivariate_normal(mu0,cov=sd0**2*np.eye(D)) for i in range(nClusts)]
#     assert len(centers) == nClusts
#     assert centers[0].shape[0] == D
#     return centers 

# def pi0_withMeans(data, sd, sd0, alpha, pi0_its, initType, rng):
#     """
#     Draw the partitions and the cluster means separately.
#     """
#     z0 = pi0(data, sd, sd0, alpha, pi0_its, initType, rng)
#     centers = init_centers(z0, data.shape[1], sd0, rng)
#     return z0, centers