github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/community/bisect_example_test.go (about) 1 // Copyright ©2016 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_test 6 7 import ( 8 "fmt" 9 "log" 10 "sort" 11 12 "golang.org/x/exp/rand" 13 14 "github.com/jingcheng-WU/gonum/graph/community" 15 "github.com/jingcheng-WU/gonum/graph/internal/ordered" 16 "github.com/jingcheng-WU/gonum/graph/simple" 17 ) 18 19 func ExampleProfile_simple() { 20 // Profile calls Modularize which implements the Louvain modularization algorithm. 21 // Since this is a randomized algorithm we use a defined random source to ensure 22 // consistency between test runs. In practice, results will not differ greatly 23 // between runs with different PRNG seeds. 24 src := rand.NewSource(1) 25 26 // Create dumbell graph: 27 // 28 // 0 4 29 // |\ /| 30 // | 2 - 3 | 31 // |/ \| 32 // 1 5 33 // 34 g := simple.NewUndirectedGraph() 35 for u, e := range smallDumbell { 36 for v := range e { 37 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 38 } 39 } 40 41 // Get the profile of internal node weight for resolutions 42 // between 0.1 and 10 using logarithmic bisection. 43 p, err := community.Profile( 44 community.ModularScore(g, community.Weight, 10, src), 45 true, 1e-3, 0.1, 10, 46 ) 47 if err != nil { 48 log.Fatal(err) 49 } 50 51 // Print out each step with communities ordered. 52 for _, d := range p { 53 comm := d.Communities() 54 for _, c := range comm { 55 sort.Sort(ordered.ByID(c)) 56 } 57 sort.Sort(ordered.BySliceIDs(comm)) 58 fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n", 59 d.Low, d.High, d.Score, comm, community.Q(g, comm, d.Low)) 60 } 61 62 // Output: 63 // Low:0.1 High:0.29 Score:14 Communities:[[0 1 2 3 4 5]] Q=0.9 64 // Low:0.29 High:2.3 Score:12 Communities:[[0 1 2] [3 4 5]] Q=0.714 65 // Low:2.3 High:3.5 Score:4 Communities:[[0 1] [2] [3] [4 5]] Q=-0.31 66 // Low:3.5 High:10 Score:0 Communities:[[0] [1] [2] [3] [4] [5]] Q=-0.607 67 } 68 69 // intset is an integer set. 70 type intset map[int]struct{} 71 72 func linksTo(i ...int) intset { 73 if len(i) == 0 { 74 return nil 75 } 76 s := make(intset) 77 for _, v := range i { 78 s[v] = struct{}{} 79 } 80 return s 81 } 82 83 var ( 84 smallDumbell = []intset{ 85 0: linksTo(1, 2), 86 1: linksTo(2), 87 2: linksTo(3), 88 3: linksTo(4, 5), 89 4: linksTo(5), 90 5: nil, 91 } 92 93 // http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html 94 middleEast = struct{ friends, complicated, enemies []intset }{ 95 // green cells 96 friends: []intset{ 97 0: nil, 98 1: linksTo(5, 7, 9, 12), 99 2: linksTo(11), 100 3: linksTo(4, 5, 10), 101 4: linksTo(3, 5, 10), 102 5: linksTo(1, 3, 4, 8, 10, 12), 103 6: nil, 104 7: linksTo(1, 12), 105 8: linksTo(5, 9, 11), 106 9: linksTo(1, 8, 12), 107 10: linksTo(3, 4, 5), 108 11: linksTo(2, 8), 109 12: linksTo(1, 5, 7, 9), 110 }, 111 112 // yellow cells 113 complicated: []intset{ 114 0: linksTo(2, 4), 115 1: linksTo(4, 8), 116 2: linksTo(0, 3, 4, 5, 8, 9), 117 3: linksTo(2, 8, 11), 118 4: linksTo(0, 1, 2, 8), 119 5: linksTo(2), 120 6: nil, 121 7: linksTo(9, 11), 122 8: linksTo(1, 2, 3, 4, 10, 12), 123 9: linksTo(2, 7, 11), 124 10: linksTo(8), 125 11: linksTo(3, 7, 9, 12), 126 12: linksTo(8, 11), 127 }, 128 129 // red cells 130 enemies: []intset{ 131 0: linksTo(1, 3, 5, 6, 7, 8, 9, 10, 11, 12), 132 1: linksTo(0, 2, 3, 6, 10, 11), 133 2: linksTo(1, 6, 7, 10, 12), 134 3: linksTo(0, 1, 6, 7, 9, 12), 135 4: linksTo(6, 7, 9, 11, 12), 136 5: linksTo(0, 6, 7, 9, 11), 137 6: linksTo(0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12), 138 7: linksTo(0, 2, 3, 4, 5, 6, 8, 10), 139 8: linksTo(0, 6, 7), 140 9: linksTo(0, 3, 4, 5, 6, 10), 141 10: linksTo(0, 1, 2, 6, 7, 9, 11, 12), 142 11: linksTo(0, 1, 4, 5, 6, 10), 143 12: linksTo(0, 2, 3, 4, 6, 10), 144 }, 145 } 146 ) 147 148 var friends, enemies *simple.WeightedUndirectedGraph 149 150 func init() { 151 friends = simple.NewWeightedUndirectedGraph(0, 0) 152 for u, e := range middleEast.friends { 153 // Ensure unconnected nodes are included. 154 if friends.Node(int64(u)) == nil { 155 friends.AddNode(simple.Node(u)) 156 } 157 for v := range e { 158 friends.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1}) 159 } 160 } 161 enemies = simple.NewWeightedUndirectedGraph(0, 0) 162 for u, e := range middleEast.enemies { 163 // Ensure unconnected nodes are included. 164 if enemies.Node(int64(u)) == nil { 165 enemies.AddNode(simple.Node(u)) 166 } 167 for v := range e { 168 enemies.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: -1}) 169 } 170 } 171 } 172 173 func ExampleProfile_multiplex() { 174 // Profile calls ModularizeMultiplex which implements the Louvain modularization 175 // algorithm. Since this is a randomized algorithm we use a defined random source 176 // to ensure consistency between test runs. In practice, results will not differ 177 // greatly between runs with different PRNG seeds. 178 src := rand.NewSource(1) 179 180 // The undirected graphs, friends and enemies, are the political relationships 181 // in the Middle East as described in the Slate article: 182 // http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html 183 g, err := community.NewUndirectedLayers(friends, enemies) 184 if err != nil { 185 log.Fatal(err) 186 } 187 weights := []float64{1, -1} 188 189 // Get the profile of internal node weight for resolutions 190 // between 0.1 and 10 using logarithmic bisection. 191 p, err := community.Profile( 192 community.ModularMultiplexScore(g, weights, true, community.WeightMultiplex, 10, src), 193 true, 1e-3, 0.1, 10, 194 ) 195 if err != nil { 196 log.Fatal(err) 197 } 198 199 // Print out each step with communities ordered. 200 for _, d := range p { 201 comm := d.Communities() 202 for _, c := range comm { 203 sort.Sort(ordered.ByID(c)) 204 } 205 sort.Sort(ordered.BySliceIDs(comm)) 206 fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n", 207 d.Low, d.High, d.Score, comm, community.QMultiplex(g, comm, weights, []float64{d.Low})) 208 } 209 210 // Output: 211 // Low:0.1 High:0.72 Score:26 Communities:[[0] [1 7 9 12] [2 8 11] [3 4 5 10] [6]] Q=[24.7 1.97] 212 // Low:0.72 High:1.1 Score:24 Communities:[[0 6] [1 7 9 12] [2 8 11] [3 4 5 10]] Q=[16.9 14.1] 213 // Low:1.1 High:1.2 Score:18 Communities:[[0 2 6 11] [1 7 9 12] [3 4 5 8 10]] Q=[9.16 25.1] 214 // Low:1.2 High:1.6 Score:10 Communities:[[0 3 4 5 6 10] [1 7 9 12] [2 8 11]] Q=[10.5 26.7] 215 // Low:1.6 High:1.6 Score:8 Communities:[[0 1 6 7 9 12] [2 8 11] [3 4 5 10]] Q=[5.56 39.8] 216 // Low:1.6 High:1.8 Score:2 Communities:[[0 2 3 4 5 6 10] [1 7 8 9 11 12]] Q=[-1.82 48.6] 217 // Low:1.8 High:2.3 Score:-6 Communities:[[0 2 3 4 5 6 8 10 11] [1 7 9 12]] Q=[-5 57.5] 218 // Low:2.3 High:2.4 Score:-10 Communities:[[0 1 2 6 7 8 9 11 12] [3 4 5 10]] Q=[-11.2 79] 219 // Low:2.4 High:4.3 Score:-52 Communities:[[0 1 2 3 4 5 6 7 8 9 10 11 12]] Q=[-46.1 117] 220 // Low:4.3 High:10 Score:-54 Communities:[[0 1 2 3 4 6 7 8 9 10 11 12] [5]] Q=[-82 254] 221 }