github.com/gopherd/gonum@v0.0.4/graph/community/k_communities.go (about)

     1  // Copyright ©2017 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package community
     6  
     7  import (
     8  	"github.com/gopherd/gonum/graph"
     9  	"github.com/gopherd/gonum/graph/internal/set"
    10  	"github.com/gopherd/gonum/graph/simple"
    11  	"github.com/gopherd/gonum/graph/topo"
    12  	"github.com/gopherd/gonum/graph/traverse"
    13  )
    14  
    15  // KCliqueCommunities returns the k-clique communties of the undirected graph g for
    16  // k greater than zero. The returned communities are identified by linkage via k-clique
    17  // adjacency, where adjacency is defined as having k-1 common nodes. KCliqueCommunities
    18  // returns a single component including the full set of nodes of g when k is 1,
    19  // and the classical connected components of g when k is 2. Note that k-clique
    20  // communities may contain common nodes from g.
    21  //
    22  // k-clique communities are described in Palla et al. doi:10.1038/nature03607.
    23  func KCliqueCommunities(k int, g graph.Undirected) [][]graph.Node {
    24  	if k < 1 {
    25  		panic("community: invalid k for k-clique communities")
    26  	}
    27  	switch k {
    28  	case 1:
    29  		return [][]graph.Node{graph.NodesOf(g.Nodes())}
    30  	case 2:
    31  		return topo.ConnectedComponents(g)
    32  	default:
    33  		cg := simple.NewUndirectedGraph()
    34  		topo.CliqueGraph(cg, g)
    35  		cc := kConnectedComponents(k, cg)
    36  
    37  		// Extract the nodes in g from cg,
    38  		// removing duplicates and separating
    39  		// cliques smaller than k into separate
    40  		// single nodes.
    41  		var kcc [][]graph.Node
    42  		single := set.NewNodes()
    43  		inCommunity := set.NewNodes()
    44  		for _, c := range cc {
    45  			nodes := set.NewNodesSize(len(c))
    46  			for _, cn := range c {
    47  				for _, n := range cn.(topo.Clique).Nodes() {
    48  					nodes.Add(n)
    49  				}
    50  			}
    51  			if len(nodes) < k {
    52  				for _, n := range nodes {
    53  					single.Add(n)
    54  				}
    55  				continue
    56  			}
    57  			var kc []graph.Node
    58  			for _, n := range nodes {
    59  				inCommunity.Add(n)
    60  				kc = append(kc, n)
    61  			}
    62  			kcc = append(kcc, kc)
    63  		}
    64  		for _, n := range single {
    65  			if !inCommunity.Has(n) {
    66  				kcc = append(kcc, []graph.Node{n})
    67  			}
    68  		}
    69  
    70  		return kcc
    71  	}
    72  }
    73  
    74  // kConnectedComponents returns the connected components of topo.Clique nodes that
    75  // are joined by k-1 underlying shared nodes in the graph that created the clique
    76  // graph cg.
    77  func kConnectedComponents(k int, cg graph.Undirected) [][]graph.Node {
    78  	var (
    79  		c  []graph.Node
    80  		cc [][]graph.Node
    81  	)
    82  	during := func(n graph.Node) {
    83  		c = append(c, n)
    84  	}
    85  	after := func() {
    86  		cc = append(cc, []graph.Node(nil))
    87  		cc[len(cc)-1] = append(cc[len(cc)-1], c...)
    88  		c = c[:0]
    89  	}
    90  	w := traverse.DepthFirst{
    91  		Traverse: func(e graph.Edge) bool {
    92  			return len(e.(topo.CliqueGraphEdge).Nodes()) >= k-1
    93  		},
    94  	}
    95  	w.WalkAll(cg, nil, after, during)
    96  
    97  	return cc
    98  }