gonum.org/v1/gonum@v0.14.0/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 "gonum.org/v1/gonum/graph" 9 "gonum.org/v1/gonum/graph/internal/set" 10 "gonum.org/v1/gonum/graph/simple" 11 "gonum.org/v1/gonum/graph/topo" 12 "gonum.org/v1/gonum/graph/traverse" 13 ) 14 15 // KCliqueCommunities returns the k-clique communities 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 }