def LogProbWC(D, Z, sizes, alpha, kappa, nu, sigsq):
    hyp = ddCRP.ComputeCachedLikelihoodTerms(kappa, nu, sigsq)
    logp = np.zeros(len(sizes))

    for i in range(len(sizes)):
        z = cluster.hierarchy.fcluster(Z, t=sizes[i], criterion='maxclust')

        sorted_i = np.argsort(z)
        sorted_z = np.sort(z)
        parcels = np.split(sorted_i, np.flatnonzero(np.diff(sorted_z)) + 1)

        # Formally we should construct a spanning tree within each cluster so
        #   that we can evaluate the probability. However, the only property of
        #   the "c" links that impacts the probability directly is the number of
        #   self-connections. So we simply add the correct number of self-
        #   connections (equal to the number of parcels) and leave the rest
        #   set to zero
        c = np.zeros(len(z))
        c[0:sizes[i]] = np.arange(sizes[i])

        logp[i] = ddCRP.FullProbabilityddCRP(D, c, parcels, alpha, hyp,
                                             StatsUtil.CheckSymApprox(D))

    return logp
def ClusterTree(D, adj_list):
    if StatsUtil.CheckSymApprox(D):
        X = D
    else:
        X = np.concatenate((D,D.transpose()),axis=1)

    # Compute squared euclidean distance Y between rows
    Qx = np.tile(np.linalg.norm(X, axis=1)**2,(X.shape[0],1))
    Y = Qx + Qx.transpose()-2*np.dot(X, X.transpose())
    Y = spatial.distance.squareform(Y,checks=False)
    Y[Y<0] = 0  # Correct for numerical errors in very similar rows
    
    # Construct adjacency matrix
    N = len(adj_list)
    A = np.zeros([N,N], dtype=bool)
    for i in range(N):
        A[i,adj_list[i]] = True
    connected = spatial.distance.squareform(A).astype(bool)
    

    # Initialize all data structures
    valid_clusts = np.ones(N, dtype=bool)   # which clusters still remain
    col_limits = np.cumsum(np.concatenate((np.array([N-2]), np.arange(N-2, 0, -1))))
    
    # During updating clusters, cluster index is constantly changing, R is
    # a index vector mapping the original index to the current (row, column)
    # index in Y.  C denotes how many points are contained in each cluster.
    m = mt.ceil(mt.sqrt(2*Y.shape[0]))
    C = np.zeros(2*m-1)
    C[0:m] = 1
    R = np.arange(m)
    all_inds = np.arange(Y.shape[0])
    conn_inds = all_inds[connected] # pairs of adjacent clusters that can be merged
    Z = np.zeros([m-1,4])

    for s in range(m-1):
        if conn_inds.size==0:
            # The graph was disconnected (e.g. two hemispheres)
            # Just add all connections to finish up cluster tree
            connected = np.zeros(len(connected))
            conn_inds = []
            valid_clust_inds = np.flatnonzero(valid_clusts)
            
            for i in valid_clust_inds:
                U = valid_clusts
                U[i] = 0
                new_conns = PdistInds(i, N, U)
                connected[new_conns] = True
                conn_inds = np.concatenate((conn_inds, new_conns))
            
            conn_inds = np.unique(conn_inds)

        # Find closest pair of clusters
        v = np.amin(Y[conn_inds])
        k = conn_inds[np.argmin(Y[conn_inds])]
    
        j = np.where(k <= col_limits)[0][0]
        i = N - (col_limits[j] - k) - 1
        
        Z[s,0:3] = np.array([R[i], R[j], v]) # Add row to output linkage
    
        # Update Y with this new cluster i containing old clusters i and j
        U = valid_clusts      
        U[np.array([i,j])] = 0
        I = PdistInds(i, N, U)
        J = PdistInds(j, N, U)
        Y[I] = ((C[R[U]]+C[R[i]])*Y[I] + 
                (C[R[U]]+C[R[j]])*Y[J] - C[R[U]]*v)/(C[R[i]]+C[R[j]]+C[R[U]])
        
        # Add j's connections to new cluster i
        new_conns = connected[J] & ~connected[I]
        connected[I] = connected[I] | new_conns
        conn_inds = np.sort(np.concatenate((conn_inds,I[new_conns])))
        
        # Remove all of j's connections from conn_inds and connected
        U[i]=1
        J = PdistInds(j, N, U)
        conn_inds = conn_inds[np.in1d(conn_inds,J, assume_unique=True,
                                                                invert=True)]
        connected[J] = np.zeros(len(J))
       
        valid_clusts[j] = 0 
        # update m, N, R
        C[m+s] = C[R[i]] + C[R[j]]
        Z[s,3] = C[m+s]
        R[i] = m+s

    Z[:,2] = np.sqrt(Z[:,2])
    return Z
Example #3
0
def ddCRP(D, adj_list, init_c, gt_z, num_passes, alpha, kappa, nu, sigsq,
          stats_interval, verbose):
    map_z = np.zeros(np.shape(D)[0])
    stats = {'times': [], 'lp': [], 'NMI': [], 'K': [], 'z': [], 'c': []}

    hyp = ComputeCachedLikelihoodTerms(kappa, nu, sigsq)
    num_el = len(adj_list)

    # Generate random initialization if not specified
    if init_c.size == 0:
        c = np.zeros(num_el)
        for i in range(num_el):
            neighbors = np.concatenate((adj_list[i], i), axis=1)
            c[i] = neighbors[rd.randint(1, len(neighbors))]
    else:
        c = init_c

    # Initialize spatial connection matrix
    G = sparse.coo_matrix((np.ones(num_el), (np.arange(num_el), c)),
                          shape=(num_el, num_el))
    K, z, parcels = ConnectedComp(G)

    sym = StatsUtil.CheckSymApprox(D)
    curr_lp = FullProbabilityddCRP(D, c, parcels, alpha, hyp, sym)

    max_lp = -float('inf')
    steps = 0
    t0 = time.clock()

    for curr_pass in range(num_passes):
        order = np.random.permutation(num_el)  # Visit elements randomly

        for i in order:
            if curr_lp > max_lp:
                max_lp = curr_lp
                map_z = z

            if steps % stats_interval == 0:
                stats = UpdateStats(stats, t0, curr_lp, K, z, c, steps, gt_z,
                                    map_z, verbose)

            # Compute change in log-prob when removing the edge c_i
            CooModifyRow(G, i, -1)
            if c[i] == i:
                # Removing self-loop, parcellation won't change
                rem_delta_lp, z_rem, parcels_rem = -mt.log(alpha), z, parcels
            else:
                K_rem, z_rem, parcels_rem = ConnectedComp(G)
                if K_rem != K:
                    # We split a cluster, compute change in likelihood
                    rem_delta_lp = -LikelihoodDiff(D, parcels_rem, z_rem[i],
                                                   z_rem[c[i]], hyp, sym)
                else:
                    rem_delta_lp = 0

            # Compute change in log-prob for each possible edge c_i
            adj_list_i = adj_list[i]
            lp = np.zeros((len(adj_list_i) + 1))
            lp[len(adj_list_i)] = mt.log(alpha)
            cached_merge = -1 * np.ones(len(adj_list_i), dtype=np.int32)
            for n_ind in range(len(adj_list_i)):
                n = adj_list_i[n_ind]
                if z_rem[n] == z_rem[c[i]]:
                    # Just undoing edge removal
                    lp[n_ind] = -rem_delta_lp - (c[i] == i) * mt.log(alpha)
                elif z_rem[n] != z_rem[i]:
                    # Proposing merge
                    # First check cache to see if this is already computed
                    prev_lp = np.flatnonzero(cached_merge == z_rem[n])
                    if prev_lp.size > 0:
                        lp[n_ind] = lp[prev_lp[0]]
                    else:
                        # This is a novel merge, compute change in likelihood
                        lp[n_ind] = LikelihoodDiff(D, parcels_rem, z_rem[i],
                                                   z_rem[n], hyp, sym)
                        cached_merge[n_ind] = z_rem[n]

            # Pick new edge proportional to probability
            new_neighbor = ChooseFromLP(lp)
            if new_neighbor < len(adj_list_i):
                c[i] = adj_list_i[new_neighbor]
            else:
                c[i] = i

            # Update likelihood and parcellation
            curr_lp = curr_lp + rem_delta_lp + lp[new_neighbor]
            CooModifyRow(G, i, c[i])
            K, z, parcels = ConnectedComp(G)
            steps = steps + 1

    stats = UpdateStats(stats, t0, curr_lp, K, z, c, steps, gt_z, map_z,
                        verbose)
    return (map_z, stats)