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