github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/community/louvain_undirected_multiplex.go (about)

     1  // Copyright ©2015 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  	"fmt"
     9  	"math"
    10  	"sort"
    11  
    12  	"golang.org/x/exp/rand"
    13  
    14  	"github.com/jingcheng-WU/gonum/graph"
    15  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    16  	"github.com/jingcheng-WU/gonum/graph/internal/set"
    17  	"github.com/jingcheng-WU/gonum/graph/iterator"
    18  )
    19  
    20  // UndirectedMultiplex is an undirected multiplex graph.
    21  type UndirectedMultiplex interface {
    22  	Multiplex
    23  
    24  	// Layer returns the lth layer of the
    25  	// multiplex graph.
    26  	Layer(l int) graph.Undirected
    27  }
    28  
    29  // qUndirectedMultiplex returns the modularity Q score of the multiplex graph layers
    30  // subdivided into the given communities at the given resolutions and weights. Q is
    31  // returned as the vector of weighted Q scores for each layer of the multiplex graph.
    32  // If communities is nil, the unclustered modularity score is returned.
    33  // If weights is nil layers are equally weighted, otherwise the length of
    34  // weights must equal the number of layers. If resolutions is nil, a resolution
    35  // of 1.0 is used for all layers, otherwise either a single element slice may be used
    36  // to specify a global resolution, or the length of resolutions must equal the number
    37  // of layers. The resolution parameter is γ as defined in Reichardt and Bornholdt
    38  // doi:10.1103/PhysRevE.74.016110.
    39  // qUndirectedMultiplex will panic if the graph has any layer weight-scaled edge with
    40  // negative edge weight.
    41  //
    42  //  Q_{layer} = w_{layer} \sum_{ij} [ A_{layer}*_{ij} - (\gamma_{layer} k_i k_j)/2m ] \delta(c_i,c_j)
    43  //
    44  // Note that Q values for multiplex graphs are not scaled by the total layer edge weight.
    45  //
    46  // graph.Undirect may be used as a shim to allow calculation of Q for
    47  // directed graphs.
    48  func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
    49  	q := make([]float64, g.Depth())
    50  	nodes := graph.NodesOf(g.Nodes())
    51  	layerWeight := 1.0
    52  	layerResolution := 1.0
    53  	if len(resolutions) == 1 {
    54  		layerResolution = resolutions[0]
    55  	}
    56  	for l := 0; l < g.Depth(); l++ {
    57  		layer := g.Layer(l)
    58  
    59  		if weights != nil {
    60  			layerWeight = weights[l]
    61  		}
    62  		if layerWeight == 0 {
    63  			continue
    64  		}
    65  
    66  		if len(resolutions) > 1 {
    67  			layerResolution = resolutions[l]
    68  		}
    69  
    70  		var weight func(xid, yid int64) float64
    71  		if layerWeight < 0 {
    72  			weight = negativeWeightFuncFor(layer)
    73  		} else {
    74  			weight = positiveWeightFuncFor(layer)
    75  		}
    76  
    77  		// Calculate the total edge weight of the layer
    78  		// and the table of penetrating edge weight sums.
    79  		var m2 float64
    80  		k := make(map[int64]float64, len(nodes))
    81  		for _, u := range nodes {
    82  			uid := u.ID()
    83  			w := weight(uid, uid)
    84  			to := layer.From(uid)
    85  			for to.Next() {
    86  				w += weight(uid, to.Node().ID())
    87  			}
    88  			m2 += w
    89  			k[uid] = w
    90  		}
    91  
    92  		if communities == nil {
    93  			var qLayer float64
    94  			for _, u := range nodes {
    95  				uid := u.ID()
    96  				kU := k[uid]
    97  				qLayer += weight(uid, uid) - layerResolution*kU*kU/m2
    98  			}
    99  			q[l] = layerWeight * qLayer
   100  			continue
   101  		}
   102  
   103  		// Iterate over the communities, calculating
   104  		// the non-self edge weights for the upper
   105  		// triangle and adjust the diagonal.
   106  		var qLayer float64
   107  		for _, c := range communities {
   108  			for i, u := range c {
   109  				uid := u.ID()
   110  				kU := k[uid]
   111  				qLayer += weight(uid, uid) - layerResolution*kU*kU/m2
   112  				for _, v := range c[i+1:] {
   113  					vid := v.ID()
   114  					qLayer += 2 * (weight(uid, vid) - layerResolution*kU*k[vid]/m2)
   115  				}
   116  			}
   117  		}
   118  		q[l] = layerWeight * qLayer
   119  	}
   120  
   121  	return q
   122  }
   123  
   124  // UndirectedLayers implements UndirectedMultiplex.
   125  type UndirectedLayers []graph.Undirected
   126  
   127  // NewUndirectedLayers returns an UndirectedLayers using the provided layers
   128  // ensuring there is a match between IDs for each layer.
   129  func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
   130  	if len(layers) == 0 {
   131  		return nil, nil
   132  	}
   133  	base := make(set.Int64s)
   134  	nodes := layers[0].Nodes()
   135  	for nodes.Next() {
   136  		base.Add(nodes.Node().ID())
   137  	}
   138  	for i, l := range layers[1:] {
   139  		next := make(set.Int64s)
   140  		nodes := l.Nodes()
   141  		for nodes.Next() {
   142  			next.Add(nodes.Node().ID())
   143  		}
   144  		if !set.Int64sEqual(next, base) {
   145  			return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
   146  		}
   147  	}
   148  	return layers, nil
   149  }
   150  
   151  // Nodes returns the nodes of the receiver.
   152  func (g UndirectedLayers) Nodes() graph.Nodes {
   153  	if len(g) == 0 {
   154  		return nil
   155  	}
   156  	return g[0].Nodes()
   157  }
   158  
   159  // Depth returns the depth of the multiplex graph.
   160  func (g UndirectedLayers) Depth() int { return len(g) }
   161  
   162  // Layer returns the lth layer of the multiplex graph.
   163  func (g UndirectedLayers) Layer(l int) graph.Undirected { return g[l] }
   164  
   165  // louvainUndirectedMultiplex returns the hierarchical modularization of g at the given resolution
   166  // using the Louvain algorithm. If all is true and g has negatively weighted layers, all
   167  // communities will be searched during the modularization. If src is nil, rand.Intn is
   168  // used as the random generator. louvainUndirectedMultiplex will panic if g has any edge with
   169  // edge weight that does not sign-match the layer weight.
   170  //
   171  // graph.Undirect may be used as a shim to allow modularization of directed graphs.
   172  func louvainUndirectedMultiplex(g UndirectedMultiplex, weights, resolutions []float64, all bool, src rand.Source) *ReducedUndirectedMultiplex {
   173  	if weights != nil && len(weights) != g.Depth() {
   174  		panic("community: weights vector length mismatch")
   175  	}
   176  	if resolutions != nil && len(resolutions) != 1 && len(resolutions) != g.Depth() {
   177  		panic("community: resolutions vector length mismatch")
   178  	}
   179  
   180  	// See louvain.tex for a detailed description
   181  	// of the algorithm used here.
   182  
   183  	c := reduceUndirectedMultiplex(g, nil, weights)
   184  	rnd := rand.Intn
   185  	if src != nil {
   186  		rnd = rand.New(src).Intn
   187  	}
   188  	for {
   189  		l := newUndirectedMultiplexLocalMover(c, c.communities, weights, resolutions, all)
   190  		if l == nil {
   191  			return c
   192  		}
   193  		if done := l.localMovingHeuristic(rnd); done {
   194  			return c
   195  		}
   196  		c = reduceUndirectedMultiplex(c, l.communities, weights)
   197  	}
   198  }
   199  
   200  // ReducedUndirectedMultiplex is an undirected graph of communities derived from a
   201  // parent graph by reduction.
   202  type ReducedUndirectedMultiplex struct {
   203  	// nodes is the set of nodes held
   204  	// by the graph. In a ReducedUndirectedMultiplex
   205  	// the node ID is the index into
   206  	// nodes.
   207  	nodes  []multiplexCommunity
   208  	layers []undirectedEdges
   209  
   210  	// communities is the community
   211  	// structure of the graph.
   212  	communities [][]graph.Node
   213  
   214  	parent *ReducedUndirectedMultiplex
   215  }
   216  
   217  var (
   218  	_ UndirectedMultiplex      = (*ReducedUndirectedMultiplex)(nil)
   219  	_ graph.WeightedUndirected = (*undirectedLayerHandle)(nil)
   220  )
   221  
   222  // Nodes returns all the nodes in the graph.
   223  func (g *ReducedUndirectedMultiplex) Nodes() graph.Nodes {
   224  	nodes := make([]graph.Node, len(g.nodes))
   225  	for i := range g.nodes {
   226  		nodes[i] = node(i)
   227  	}
   228  	return iterator.NewOrderedNodes(nodes)
   229  }
   230  
   231  // Depth returns the number of layers in the multiplex graph.
   232  func (g *ReducedUndirectedMultiplex) Depth() int { return len(g.layers) }
   233  
   234  // Layer returns the lth layer of the multiplex graph.
   235  func (g *ReducedUndirectedMultiplex) Layer(l int) graph.Undirected {
   236  	return undirectedLayerHandle{multiplex: g, layer: l}
   237  }
   238  
   239  // Communities returns the community memberships of the nodes in the
   240  // graph used to generate the reduced graph.
   241  func (g *ReducedUndirectedMultiplex) Communities() [][]graph.Node {
   242  	communities := make([][]graph.Node, len(g.communities))
   243  	if g.parent == nil {
   244  		for i, members := range g.communities {
   245  			comm := make([]graph.Node, len(members))
   246  			for j, n := range members {
   247  				nodes := g.nodes[n.ID()].nodes
   248  				if len(nodes) != 1 {
   249  					panic("community: unexpected number of nodes in base graph community")
   250  				}
   251  				comm[j] = nodes[0]
   252  			}
   253  			communities[i] = comm
   254  		}
   255  		return communities
   256  	}
   257  	sub := g.parent.Communities()
   258  	for i, members := range g.communities {
   259  		var comm []graph.Node
   260  		for _, n := range members {
   261  			comm = append(comm, sub[n.ID()]...)
   262  		}
   263  		communities[i] = comm
   264  	}
   265  	return communities
   266  }
   267  
   268  // Structure returns the community structure of the current level of
   269  // the module clustering. The first index of the returned value
   270  // corresponds to the index of the nodes in the next higher level if
   271  // it exists. The returned value should not be mutated.
   272  func (g *ReducedUndirectedMultiplex) Structure() [][]graph.Node {
   273  	return g.communities
   274  }
   275  
   276  // Expanded returns the next lower level of the module clustering or nil
   277  // if at the lowest level.
   278  func (g *ReducedUndirectedMultiplex) Expanded() ReducedMultiplex {
   279  	return g.parent
   280  }
   281  
   282  // reduceUndirectedMultiplex returns a reduced graph constructed from g divided
   283  // into the given communities. The communities value is mutated
   284  // by the call to reduceUndirectedMultiplex. If communities is nil and g is a
   285  // ReducedUndirectedMultiplex, it is returned unaltered.
   286  func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights []float64) *ReducedUndirectedMultiplex {
   287  	if communities == nil {
   288  		if r, ok := g.(*ReducedUndirectedMultiplex); ok {
   289  			return r
   290  		}
   291  
   292  		nodes := graph.NodesOf(g.Nodes())
   293  		// TODO(kortschak) This sort is necessary really only
   294  		// for testing. In practice we would not be using the
   295  		// community provided by the user for a Q calculation.
   296  		// Probably we should use a function to map the
   297  		// communities in the test sets to the remapped order.
   298  		sort.Sort(ordered.ByID(nodes))
   299  		communities = make([][]graph.Node, len(nodes))
   300  		for i := range nodes {
   301  			communities[i] = []graph.Node{node(i)}
   302  		}
   303  
   304  		r := ReducedUndirectedMultiplex{
   305  			nodes:       make([]multiplexCommunity, len(nodes)),
   306  			layers:      make([]undirectedEdges, g.Depth()),
   307  			communities: communities,
   308  		}
   309  		communityOf := make(map[int64]int, len(nodes))
   310  		for i, n := range nodes {
   311  			r.nodes[i] = multiplexCommunity{id: i, nodes: []graph.Node{n}, weights: make([]float64, depth(weights))}
   312  			communityOf[n.ID()] = i
   313  		}
   314  		for i := range r.layers {
   315  			r.layers[i] = undirectedEdges{
   316  				edges:   make([][]int, len(nodes)),
   317  				weights: make(map[[2]int]float64),
   318  			}
   319  		}
   320  		w := 1.0
   321  		for l := 0; l < g.Depth(); l++ {
   322  			layer := g.Layer(l)
   323  			if weights != nil {
   324  				w = weights[l]
   325  			}
   326  			if w == 0 {
   327  				continue
   328  			}
   329  			var sign float64
   330  			var weight func(xid, yid int64) float64
   331  			if w < 0 {
   332  				sign, weight = -1, negativeWeightFuncFor(layer)
   333  			} else {
   334  				sign, weight = 1, positiveWeightFuncFor(layer)
   335  			}
   336  			for _, u := range nodes {
   337  				var out []int
   338  				uid := u.ID()
   339  				ucid := communityOf[uid]
   340  				to := layer.From(uid)
   341  				for to.Next() {
   342  					vid := to.Node().ID()
   343  					vcid := communityOf[vid]
   344  					if vcid != ucid {
   345  						out = append(out, vcid)
   346  					}
   347  					if ucid < vcid {
   348  						// Only store the weight once.
   349  						r.layers[l].weights[[2]int{ucid, vcid}] = sign * weight(uid, vid)
   350  					}
   351  				}
   352  				r.layers[l].edges[ucid] = out
   353  			}
   354  		}
   355  		return &r
   356  	}
   357  
   358  	// Remove zero length communities destructively.
   359  	var commNodes int
   360  	for i := 0; i < len(communities); {
   361  		comm := communities[i]
   362  		if len(comm) == 0 {
   363  			communities[i] = communities[len(communities)-1]
   364  			communities[len(communities)-1] = nil
   365  			communities = communities[:len(communities)-1]
   366  		} else {
   367  			commNodes += len(comm)
   368  			i++
   369  		}
   370  	}
   371  
   372  	r := ReducedUndirectedMultiplex{
   373  		nodes:  make([]multiplexCommunity, len(communities)),
   374  		layers: make([]undirectedEdges, g.Depth()),
   375  	}
   376  	communityOf := make(map[int64]int, commNodes)
   377  	for i, comm := range communities {
   378  		r.nodes[i] = multiplexCommunity{id: i, nodes: comm, weights: make([]float64, depth(weights))}
   379  		for _, n := range comm {
   380  			communityOf[n.ID()] = i
   381  		}
   382  	}
   383  	for i := range r.layers {
   384  		r.layers[i] = undirectedEdges{
   385  			edges:   make([][]int, len(communities)),
   386  			weights: make(map[[2]int]float64),
   387  		}
   388  	}
   389  	r.communities = make([][]graph.Node, len(communities))
   390  	for i := range r.communities {
   391  		r.communities[i] = []graph.Node{node(i)}
   392  	}
   393  	if g, ok := g.(*ReducedUndirectedMultiplex); ok {
   394  		// Make sure we retain the truncated
   395  		// community structure.
   396  		g.communities = communities
   397  		r.parent = g
   398  	}
   399  	w := 1.0
   400  	for l := 0; l < g.Depth(); l++ {
   401  		layer := g.Layer(l)
   402  		if weights != nil {
   403  			w = weights[l]
   404  		}
   405  		if w == 0 {
   406  			continue
   407  		}
   408  		var sign float64
   409  		var weight func(xid, yid int64) float64
   410  		if w < 0 {
   411  			sign, weight = -1, negativeWeightFuncFor(layer)
   412  		} else {
   413  			sign, weight = 1, positiveWeightFuncFor(layer)
   414  		}
   415  		for ucid, comm := range communities {
   416  			var out []int
   417  			for i, u := range comm {
   418  				uid := u.ID()
   419  				r.nodes[ucid].weights[l] += sign * weight(uid, uid)
   420  				for _, v := range comm[i+1:] {
   421  					r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID())
   422  				}
   423  				to := layer.From(uid)
   424  				for to.Next() {
   425  					vid := to.Node().ID()
   426  					vcid := communityOf[vid]
   427  					found := false
   428  					for _, e := range out {
   429  						if e == vcid {
   430  							found = true
   431  							break
   432  						}
   433  					}
   434  					if !found && vcid != ucid {
   435  						out = append(out, vcid)
   436  					}
   437  					if ucid < vcid {
   438  						// Only store the weight once.
   439  						r.layers[l].weights[[2]int{ucid, vcid}] += sign * weight(uid, vid)
   440  					}
   441  				}
   442  			}
   443  			r.layers[l].edges[ucid] = out
   444  		}
   445  	}
   446  	return &r
   447  }
   448  
   449  // undirectedLayerHandle is a handle to a multiplex graph layer.
   450  type undirectedLayerHandle struct {
   451  	// multiplex is the complete
   452  	// multiplex graph.
   453  	multiplex *ReducedUndirectedMultiplex
   454  
   455  	// layer is an index into the
   456  	// multiplex for the current
   457  	// layer.
   458  	layer int
   459  }
   460  
   461  // Node returns the node with the given ID if it exists in the graph,
   462  // and nil otherwise.
   463  func (g undirectedLayerHandle) Node(id int64) graph.Node {
   464  	if g.has(id) {
   465  		return g.multiplex.nodes[id]
   466  	}
   467  	return nil
   468  }
   469  
   470  // has returns whether the node exists within the graph.
   471  func (g undirectedLayerHandle) has(id int64) bool {
   472  	return 0 <= id && id < int64(len(g.multiplex.nodes))
   473  }
   474  
   475  // Nodes returns all the nodes in the graph.
   476  func (g undirectedLayerHandle) Nodes() graph.Nodes {
   477  	nodes := make([]graph.Node, len(g.multiplex.nodes))
   478  	for i := range g.multiplex.nodes {
   479  		nodes[i] = node(i)
   480  	}
   481  	return iterator.NewOrderedNodes(nodes)
   482  }
   483  
   484  // From returns all nodes in g that can be reached directly from u.
   485  func (g undirectedLayerHandle) From(uid int64) graph.Nodes {
   486  	out := g.multiplex.layers[g.layer].edges[uid]
   487  	nodes := make([]graph.Node, len(out))
   488  	for i, vid := range out {
   489  		nodes[i] = g.multiplex.nodes[vid]
   490  	}
   491  	return iterator.NewOrderedNodes(nodes)
   492  }
   493  
   494  // HasEdgeBetween returns whether an edge exists between nodes x and y.
   495  func (g undirectedLayerHandle) HasEdgeBetween(xid, yid int64) bool {
   496  	if xid == yid || !isValidID(xid) || !isValidID(yid) {
   497  		return false
   498  	}
   499  	if xid > yid {
   500  		xid, yid = yid, xid
   501  	}
   502  	_, ok := g.multiplex.layers[g.layer].weights[[2]int{int(xid), int(yid)}]
   503  	return ok
   504  }
   505  
   506  // Edge returns the edge from u to v if such an edge exists and nil otherwise.
   507  // The node v must be directly reachable from u as defined by the From method.
   508  func (g undirectedLayerHandle) Edge(uid, vid int64) graph.Edge {
   509  	return g.WeightedEdgeBetween(uid, vid)
   510  }
   511  
   512  // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise.
   513  // The node v must be directly reachable from u as defined by the From method.
   514  func (g undirectedLayerHandle) WeightedEdge(uid, vid int64) graph.WeightedEdge {
   515  	return g.WeightedEdgeBetween(uid, vid)
   516  }
   517  
   518  // EdgeBetween returns the edge between nodes x and y.
   519  func (g undirectedLayerHandle) EdgeBetween(xid, yid int64) graph.Edge {
   520  	return g.WeightedEdgeBetween(xid, yid)
   521  }
   522  
   523  // WeightedEdgeBetween returns the weighted edge between nodes x and y.
   524  func (g undirectedLayerHandle) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge {
   525  	if xid == yid || !isValidID(xid) || !isValidID(yid) {
   526  		return nil
   527  	}
   528  	if yid < xid {
   529  		xid, yid = yid, xid
   530  	}
   531  	w, ok := g.multiplex.layers[g.layer].weights[[2]int{int(xid), int(yid)}]
   532  	if !ok {
   533  		return nil
   534  	}
   535  	return multiplexEdge{from: g.multiplex.nodes[xid], to: g.multiplex.nodes[yid], weight: w}
   536  }
   537  
   538  // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge.
   539  // If x and y are the same node the internal node weight is returned. If there is no joining
   540  // edge between the two nodes the weight value returned is zero. Weight returns true if an edge
   541  // exists between x and y or if x and y have the same ID, false otherwise.
   542  func (g undirectedLayerHandle) Weight(xid, yid int64) (w float64, ok bool) {
   543  	if !isValidID(xid) || !isValidID(yid) {
   544  		return 0, false
   545  	}
   546  	if xid == yid {
   547  		return g.multiplex.nodes[xid].weights[g.layer], true
   548  	}
   549  	if xid > yid {
   550  		xid, yid = yid, xid
   551  	}
   552  	w, ok = g.multiplex.layers[g.layer].weights[[2]int{int(xid), int(yid)}]
   553  	return w, ok
   554  }
   555  
   556  // undirectedMultiplexLocalMover is a step in graph modularity optimization.
   557  type undirectedMultiplexLocalMover struct {
   558  	g *ReducedUndirectedMultiplex
   559  
   560  	// nodes is the set of working nodes.
   561  	nodes []graph.Node
   562  	// edgeWeightOf is the weighted degree
   563  	// of each node indexed by ID.
   564  	edgeWeightOf [][]float64
   565  
   566  	// m2 is the total sum of
   567  	// edge weights in g.
   568  	m2 []float64
   569  
   570  	// weight is the weight function
   571  	// provided by g or a function
   572  	// that returns the Weight value
   573  	// of the non-nil edge between x
   574  	// and y.
   575  	weight []func(xid, yid int64) float64
   576  
   577  	// communities is the current
   578  	// division of g.
   579  	communities [][]graph.Node
   580  	// memberships is a mapping between
   581  	// node ID and community membership.
   582  	memberships []int
   583  
   584  	// resolution is the Reichardt and
   585  	// Bornholdt γ parameter as defined
   586  	// in doi:10.1103/PhysRevE.74.016110.
   587  	resolutions []float64
   588  
   589  	// weights is the layer weights for
   590  	// the modularisation.
   591  	weights []float64
   592  
   593  	// searchAll specifies whether the local
   594  	// mover should consider non-connected
   595  	// communities during the local moving
   596  	// heuristic.
   597  	searchAll bool
   598  
   599  	// moved indicates that a call to
   600  	// move has been made since the last
   601  	// call to shuffle.
   602  	moved bool
   603  
   604  	// changed indicates that a move
   605  	// has been made since the creation
   606  	// of the local mover.
   607  	changed bool
   608  }
   609  
   610  // newUndirectedMultiplexLocalMover returns a new undirectedMultiplexLocalMover initialized with
   611  // the graph g, a set of communities and a modularity resolution parameter. The
   612  // node IDs of g must be contiguous in [0,n) where n is the number of nodes.
   613  // If g has a zero edge weight sum, nil is returned.
   614  func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover {
   615  	nodes := graph.NodesOf(g.Nodes())
   616  	l := undirectedMultiplexLocalMover{
   617  		g:            g,
   618  		nodes:        nodes,
   619  		edgeWeightOf: make([][]float64, g.Depth()),
   620  		m2:           make([]float64, g.Depth()),
   621  		communities:  communities,
   622  		memberships:  make([]int, len(nodes)),
   623  		resolutions:  resolutions,
   624  		weights:      weights,
   625  		weight:       make([]func(xid, yid int64) float64, g.Depth()),
   626  	}
   627  
   628  	// Calculate the total edge weight of the graph
   629  	// and degree weights for each node.
   630  	var zero int
   631  	for i := 0; i < g.Depth(); i++ {
   632  		l.edgeWeightOf[i] = make([]float64, len(nodes))
   633  		var weight func(xid, yid int64) float64
   634  
   635  		if weights != nil {
   636  			if weights[i] == 0 {
   637  				zero++
   638  				continue
   639  			}
   640  			if weights[i] < 0 {
   641  				weight = negativeWeightFuncFor(g.Layer(i))
   642  				l.searchAll = all
   643  			} else {
   644  				weight = positiveWeightFuncFor(g.Layer(i))
   645  			}
   646  		} else {
   647  			weight = positiveWeightFuncFor(g.Layer(i))
   648  		}
   649  
   650  		l.weight[i] = weight
   651  		layer := g.Layer(i)
   652  		for _, u := range l.nodes {
   653  			uid := u.ID()
   654  			w := weight(uid, uid)
   655  			to := layer.From(uid)
   656  			for to.Next() {
   657  				w += weight(uid, to.Node().ID())
   658  			}
   659  			l.edgeWeightOf[i][uid] = w
   660  			l.m2[i] += w
   661  		}
   662  		if l.m2[i] == 0 {
   663  			zero++
   664  		}
   665  	}
   666  	if zero == g.Depth() {
   667  		return nil
   668  	}
   669  
   670  	// Assign membership mappings.
   671  	for i, c := range communities {
   672  		for _, u := range c {
   673  			l.memberships[u.ID()] = i
   674  		}
   675  	}
   676  
   677  	return &l
   678  }
   679  
   680  // localMovingHeuristic performs the Louvain local moving heuristic until
   681  // no further moves can be made. It returns a boolean indicating that the
   682  // undirectedMultiplexLocalMover has not made any improvement to the community
   683  // structure and so the Louvain algorithm is done.
   684  func (l *undirectedMultiplexLocalMover) localMovingHeuristic(rnd func(int) int) (done bool) {
   685  	for {
   686  		l.shuffle(rnd)
   687  		for _, n := range l.nodes {
   688  			dQ, dst, src := l.deltaQ(n)
   689  			if dQ <= deltaQtol {
   690  				continue
   691  			}
   692  			l.move(dst, src)
   693  		}
   694  		if !l.moved {
   695  			return !l.changed
   696  		}
   697  	}
   698  }
   699  
   700  // shuffle performs a Fisher-Yates shuffle on the nodes held by the
   701  // undirectedMultiplexLocalMover using the random source rnd which should return
   702  // an integer in the range [0,n).
   703  func (l *undirectedMultiplexLocalMover) shuffle(rnd func(n int) int) {
   704  	l.moved = false
   705  	for i := range l.nodes[:len(l.nodes)-1] {
   706  		j := i + rnd(len(l.nodes)-i)
   707  		l.nodes[i], l.nodes[j] = l.nodes[j], l.nodes[i]
   708  	}
   709  }
   710  
   711  // move moves the node at src to the community at dst.
   712  func (l *undirectedMultiplexLocalMover) move(dst int, src commIdx) {
   713  	l.moved = true
   714  	l.changed = true
   715  
   716  	srcComm := l.communities[src.community]
   717  	n := srcComm[src.node]
   718  
   719  	l.memberships[n.ID()] = dst
   720  
   721  	l.communities[dst] = append(l.communities[dst], n)
   722  	srcComm[src.node], srcComm[len(srcComm)-1] = srcComm[len(srcComm)-1], nil
   723  	l.communities[src.community] = srcComm[:len(srcComm)-1]
   724  }
   725  
   726  // deltaQ returns the highest gain in modularity attainable by moving
   727  // n from its current community to another connected community and
   728  // the index of the chosen destination. The index into the
   729  // undirectedMultiplexLocalMover's communities field is returned in src if n
   730  // is in communities.
   731  func (l *undirectedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src commIdx) {
   732  	id := n.ID()
   733  
   734  	var iterator minTaker
   735  	if l.searchAll {
   736  		iterator = &dense{n: len(l.communities)}
   737  	} else {
   738  		// Find communities connected to n.
   739  		connected := make(set.Ints)
   740  		// The following for loop is equivalent to:
   741  		//
   742  		//  for i := 0; i < l.g.Depth(); i++ {
   743  		//  	for _, v := range l.g.Layer(i).From(n) {
   744  		//  		connected.Add(l.memberships[v.ID()])
   745  		//  	}
   746  		//  }
   747  		//
   748  		// This is done to avoid an allocation for
   749  		// each layer.
   750  		for _, layer := range l.g.layers {
   751  			for _, vid := range layer.edges[id] {
   752  				connected.Add(l.memberships[vid])
   753  			}
   754  		}
   755  		// Insert the node's own community.
   756  		connected.Add(l.memberships[id])
   757  		iterator = newSlice(connected)
   758  	}
   759  
   760  	// Calculate the highest modularity gain
   761  	// from moving into another community and
   762  	// keep the index of that community.
   763  	var dQremove float64
   764  	dQadd, dst, src := math.Inf(-1), -1, commIdx{-1, -1}
   765  	var i int
   766  	for iterator.TakeMin(&i) {
   767  		c := l.communities[i]
   768  		var removal bool
   769  		var _dQadd float64
   770  		for layer := 0; layer < l.g.Depth(); layer++ {
   771  			m2 := l.m2[layer]
   772  			if m2 == 0 {
   773  				// Do not consider layers with zero sum edge weight.
   774  				continue
   775  			}
   776  			w := 1.0
   777  			if l.weights != nil {
   778  				w = l.weights[layer]
   779  			}
   780  			if w == 0 {
   781  				// Do not consider layers with zero weighting.
   782  				continue
   783  			}
   784  
   785  			var k_aC, sigma_totC float64 // C is a substitution for ^𝛼 or ^𝛽.
   786  			removal = false
   787  			for j, u := range c {
   788  				uid := u.ID()
   789  				if uid == id {
   790  					// Only mark and check src community on the first layer.
   791  					if layer == 0 {
   792  						if src.community != -1 {
   793  							panic("community: multiple sources")
   794  						}
   795  						src = commIdx{i, j}
   796  					}
   797  					removal = true
   798  				}
   799  
   800  				k_aC += l.weight[layer](id, uid)
   801  				// sigma_totC could be kept for each community
   802  				// and updated for moves, changing the calculation
   803  				// of sigma_totC here from O(n_c) to O(1), but
   804  				// in practice the time savings do not appear
   805  				// to be compelling and do not make up for the
   806  				// increase in code complexity and space required.
   807  				sigma_totC += l.edgeWeightOf[layer][uid]
   808  			}
   809  
   810  			a_aa := l.weight[layer](id, id)
   811  			k_a := l.edgeWeightOf[layer][id]
   812  			gamma := 1.0
   813  			if l.resolutions != nil {
   814  				if len(l.resolutions) == 1 {
   815  					gamma = l.resolutions[0]
   816  				} else {
   817  					gamma = l.resolutions[layer]
   818  				}
   819  			}
   820  
   821  			// See louvain.tex for a derivation of these equations.
   822  			// The weighting term, w, is described in V Traag,
   823  			// "Algorithms and dynamical models for communities and
   824  			// reputation in social networks", chapter 5.
   825  			// http://www.traag.net/wp/wp-content/papercite-data/pdf/traag_algorithms_2013.pdf
   826  			switch {
   827  			case removal:
   828  				// The community c was the current community,
   829  				// so calculate the change due to removal.
   830  				dQremove += w * (k_aC /*^𝛼*/ - a_aa - gamma*k_a*(sigma_totC /*^𝛼*/ -k_a)/m2)
   831  
   832  			default:
   833  				// Otherwise calculate the change due to an addition
   834  				// to c.
   835  				_dQadd += w * (k_aC /*^𝛽*/ - gamma*k_a*sigma_totC /*^𝛽*/ /m2)
   836  			}
   837  		}
   838  		if !removal && _dQadd > dQadd {
   839  			dQadd = _dQadd
   840  			dst = i
   841  		}
   842  	}
   843  
   844  	return 2 * (dQadd - dQremove), dst, src
   845  }