func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights []float64) *ReducedUndirectedMultiplex {
	if communities == nil {
		if r, ok := g.(*ReducedUndirectedMultiplex); ok {
			return r
		}
		nodes := g.Nodes()
		sort.Sort(ordered.ByID(nodes))
		communities = make([][]graph.Node, len(nodes))
		for i := range nodes {
			communities[i] = []graph.Node{node(i)}
		}
		r := ReducedUndirectedMultiplex{
			nodes:       make([]multiplexCommunity, len(nodes)),
			layers:      make([]undirectedEdges, g.Depth()),
			communities: communities,
		}
		communityOf := make(map[int64]int, len(nodes))
		for i, n := range nodes {
			r.nodes[i] = multiplexCommunity{id: i, nodes: []graph.Node{n}, weights: make([]float64, depth(weights))}
			communityOf[n.ID()] = i
		}
		for i := range r.layers {
			r.layers[i] = undirectedEdges{
				edges:   make([][]int, len(nodes)),
				weights: make(map[[2]int]float64),
			}
		}
		w := 1.0
		for l := 0; l < g.Depth(); l++ {
			layer := g.Layer(l)
			if weights != nil {
				w = weights[l]
			}
			if w == 0 {
				continue
			}
			var sign float64
			var weight func(x, y graph.Node) float64
			if w < 0 {
				sign, weight = -1, negativeWeightFuncFor(layer)
			} else {
				sign, weight = 1, positiveWeightFuncFor(layer)
			}
			for _, u := range nodes {
				var out []int
				uid := communityOf[u.ID()]
				for _, v := range layer.From(u) {
					vid := communityOf[v.ID()]
					if vid != uid {
						out = append(out, vid)
					}
					if uid < vid {
						r.layers[l].weights[[2]int{uid, vid}] = sign * weight(u, v)
					}
				}
				r.layers[l].edges[uid] = out
			}
		}
		return &r
	}
	var commNodes int
	for i := 0; i < len(communities); {
		comm := communities[i]
		if len(comm) == 0 {
			communities[i] = communities[len(communities)-1]
			communities[len(communities)-1] = nil
			communities = communities[:len(communities)-1]
		} else {
			commNodes += len(comm)
			i++
		}
	}
	r := ReducedUndirectedMultiplex{
		nodes:  make([]multiplexCommunity, len(communities)),
		layers: make([]undirectedEdges, g.Depth()),
	}
	communityOf := make(map[int64]int, commNodes)
	for i, comm := range communities {
		r.nodes[i] = multiplexCommunity{id: i, nodes: comm, weights: make([]float64, depth(weights))}
		for _, n := range comm {
			communityOf[n.ID()] = i
		}
	}
	for i := range r.layers {
		r.layers[i] = undirectedEdges{
			edges:   make([][]int, len(communities)),
			weights: make(map[[2]int]float64),
		}
	}
	r.communities = make([][]graph.Node, len(communities))
	for i := range r.communities {
		r.communities[i] = []graph.Node{node(i)}
	}
	if g, ok := g.(*ReducedUndirectedMultiplex); ok {
		g.communities = communities
		r.parent = g
	}
	w := 1.0
	for l := 0; l < g.Depth(); l++ {
		layer := g.Layer(l)
		if weights != nil {
			w = weights[l]
		}
		if w == 0 {
			continue
		}
		var sign float64
		var weight func(x, y graph.Node) float64
		if w < 0 {
			sign, weight = -1, negativeWeightFuncFor(layer)
		} else {
			sign, weight = 1, positiveWeightFuncFor(layer)
		}
		for uid, comm := range communities {
			var out []int
			for i, u := range comm {
				r.nodes[uid].weights[l] += sign * weight(u, u)
				for _, v := range comm[i+1:] {
					r.nodes[uid].weights[l] += 2 * sign * weight(u, v)
				}
				for _, v := range layer.From(u) {
					vid := communityOf[v.ID()]
					found := false
					for _, e := range out {
						if e == vid {
							found = true
							break
						}
					}
					if !found && vid != uid {
						out = append(out, vid)
					}
					if uid < vid {
						r.layers[l].weights[[2]int{uid, vid}] += sign * weight(u, v)
					}
				}
			}
			r.layers[l].edges[uid] = out
		}
	}
	return &r
}
reduceUndirectedMultiplex returns a reduced graph constructed from g divided
into the given communities. The communities value is mutated
by the call to reduceUndirectedMultiplex. If communities is nil and g is a
ReducedUndirectedMultiplex, it is returned unaltered.
package community
import (
	"fmt"
	"math"
	"math/rand"
	"sort"
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/ordered"
	"gonum.org/v1/gonum/graph/internal/set"
)
type UndirectedMultiplex interface {
	Multiplex
	Layer(l int) graph.Undirected
}
func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
	q := make([]float64, g.Depth())
	nodes := g.Nodes()
	layerWeight := 1.0
	layerResolution := 1.0
	if len(resolutions) == 1 {
		layerResolution = resolutions[0]
	}
	for l := 0; l < g.Depth(); l++ {
		layer := g.Layer(l)
		if weights != nil {
			layerWeight = weights[l]
		}
		if layerWeight == 0 {
			continue
		}
		if len(resolutions) > 1 {
			layerResolution = resolutions[l]
		}
		var weight func(x, y graph.Node) float64
		if layerWeight < 0 {
			weight = negativeWeightFuncFor(layer)
		} else {
			weight = positiveWeightFuncFor(layer)
		}
		var m2 float64
		k := make(map[int64]float64, len(nodes))
		for _, u := range nodes {
			w := weight(u, u)
			for _, v := range layer.From(u) {
				w += weight(u, v)
			}
			m2 += w
			k[u.ID()] = w
		}
		if communities == nil {
			var qLayer float64
			for _, u := range nodes {
				kU := k[u.ID()]
				qLayer += weight(u, u) - layerResolution*kU*kU/m2
			}
			q[l] = layerWeight * qLayer
			continue
		}
		var qLayer float64
		for _, c := range communities {
			for i, u := range c {
				kU := k[u.ID()]
				qLayer += weight(u, u) - layerResolution*kU*kU/m2
				for _, v := range c[i+1:] {
					qLayer += 2 * (weight(u, v) - layerResolution*kU*k[v.ID()]/m2)
				}
			}
		}
		q[l] = layerWeight * qLayer
	}
	return q
}
type UndirectedLayers []graph.Undirected
func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
	if len(layers) == 0 {
		return nil, nil
	}
	base := make(set.Int64s)
	for _, n := range layers[0].Nodes() {
		base.Add(n.ID())
	}
	for i, l := range layers[1:] {
		next := make(set.Int64s)
		for _, n := range l.Nodes() {
			next.Add(n.ID())
		}
		if !set.Int64sEqual(next, base) {
			return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
		}
	}
	return layers, nil
}
func (g UndirectedLayers) Nodes() []graph.Node {
	if len(g) == 0 {
		return nil
	}
	return g[0].Nodes()
}
func (g UndirectedLayers) Depth() int { return len(g) }
func (g UndirectedLayers) Layer(l int) graph.Undirected { return g[l] }
func louvainUndirectedMultiplex(g UndirectedMultiplex, weights, resolutions []float64, all bool, src *rand.Rand) *ReducedUndirectedMultiplex {
	if weights != nil && len(weights) != g.Depth() {
		panic("community: weights vector length mismatch")
	}
	if resolutions != nil && len(resolutions) != 1 && len(resolutions) != g.Depth() {
		panic("community: resolutions vector length mismatch")
	}
	c := reduceUndirectedMultiplex(g, nil, weights)
	rnd := rand.Intn
	if src != nil {
		rnd = src.Intn
	}
	for {
		l := newUndirectedMultiplexLocalMover(c, c.communities, weights, resolutions, all)
		if l == nil {
			return c
		}
		if done := l.localMovingHeuristic(rnd); done {
			return c
		}
		c = reduceUndirectedMultiplex(c, l.communities, weights)
	}
}
type ReducedUndirectedMultiplex struct {
	nodes  []multiplexCommunity
	layers []undirectedEdges
	communities [][]graph.Node
	parent *ReducedUndirectedMultiplex
}
var (
	_ UndirectedMultiplex      = (*ReducedUndirectedMultiplex)(nil)
	_ graph.WeightedUndirected = (*undirectedLayerHandle)(nil)
)
func (g *ReducedUndirectedMultiplex) Nodes() []graph.Node {
	nodes := make([]graph.Node, len(g.nodes))
	for i := range g.nodes {
		nodes[i] = node(i)
	}
	return nodes
}
func (g *ReducedUndirectedMultiplex) Depth() int { return len(g.layers) }
func (g *ReducedUndirectedMultiplex) Layer(l int) graph.Undirected {
	return undirectedLayerHandle{multiplex: g, layer: l}
}
func (g *ReducedUndirectedMultiplex) Communities() [][]graph.Node {
	communities := make([][]graph.Node, len(g.communities))
	if g.parent == nil {
		for i, members := range g.communities {
			comm := make([]graph.Node, len(members))
			for j, n := range members {
				nodes := g.nodes[n.ID()].nodes
				if len(nodes) != 1 {
					panic("community: unexpected number of nodes in base graph community")
				}
				comm[j] = nodes[0]
			}
			communities[i] = comm
		}
		return communities
	}
	sub := g.parent.Communities()
	for i, members := range g.communities {
		var comm []graph.Node
		for _, n := range members {
			comm = append(comm, sub[n.ID()]...)
		}
		communities[i] = comm
	}
	return communities
}
func (g *ReducedUndirectedMultiplex) Structure() [][]graph.Node {
	return g.communities
}
func (g *ReducedUndirectedMultiplex) Expanded() ReducedMultiplex {
	return g.parent
}