mirror of
https://github.com/gonum/gonum.git
synced 2025-10-19 21:44:41 +08:00
251 lines
5.9 KiB
Go
251 lines
5.9 KiB
Go
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package topo
|
|
|
|
import (
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
|
"gonum.org/v1/gonum/graph/internal/set"
|
|
)
|
|
|
|
// DegeneracyOrdering returns the degeneracy ordering and the k-cores of
|
|
// the undirected graph g.
|
|
func DegeneracyOrdering(g graph.Undirected) (order []graph.Node, cores [][]graph.Node) {
|
|
order, offsets := degeneracyOrdering(g)
|
|
|
|
ordered.Reverse(order)
|
|
cores = make([][]graph.Node, len(offsets))
|
|
offset := len(order)
|
|
for i, n := range offsets {
|
|
cores[i] = order[offset-n : offset]
|
|
offset -= n
|
|
}
|
|
return order, cores
|
|
}
|
|
|
|
// KCore returns the k-core of the undirected graph g with nodes in an
|
|
// optimal ordering for the coloring number.
|
|
func KCore(k int, g graph.Undirected) []graph.Node {
|
|
order, offsets := degeneracyOrdering(g)
|
|
|
|
var offset int
|
|
for _, n := range offsets[:k] {
|
|
offset += n
|
|
}
|
|
core := make([]graph.Node, len(order)-offset)
|
|
copy(core, order[offset:])
|
|
return core
|
|
}
|
|
|
|
// degeneracyOrdering is the common code for DegeneracyOrdering and KCore. It
|
|
// returns l, the nodes of g in optimal ordering for coloring number and
|
|
// s, a set of relative offsets into l for each k-core, where k is an index
|
|
// into s.
|
|
func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
|
|
nodes := graph.NodesOf(g.Nodes())
|
|
|
|
// The algorithm used here is essentially as described at
|
|
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
|
|
|
|
// Initialize an output list L in return parameters.
|
|
|
|
// Compute a number d_v for each vertex v in G,
|
|
// the number of neighbors of v that are not already in L.
|
|
// Initially, these numbers are just the degrees of the vertices.
|
|
dv := make(map[int64]int, len(nodes))
|
|
var (
|
|
maxDegree int
|
|
neighbours = make(map[int64][]graph.Node)
|
|
)
|
|
for _, n := range nodes {
|
|
id := n.ID()
|
|
adj := graph.NodesOf(g.From(id))
|
|
neighbours[id] = adj
|
|
dv[id] = len(adj)
|
|
if len(adj) > maxDegree {
|
|
maxDegree = len(adj)
|
|
}
|
|
}
|
|
|
|
// Initialize an array D such that D[i] contains a list of the
|
|
// vertices v that are not already in L for which d_v = i.
|
|
d := make([][]graph.Node, maxDegree+1)
|
|
for _, n := range nodes {
|
|
deg := dv[n.ID()]
|
|
d[deg] = append(d[deg], n)
|
|
}
|
|
|
|
// Initialize k to 0.
|
|
k := 0
|
|
// Repeat n times:
|
|
s = []int{0}
|
|
for range nodes {
|
|
// Scan the array cells D[0], D[1], ... until
|
|
// finding an i for which D[i] is nonempty.
|
|
var (
|
|
i int
|
|
di []graph.Node
|
|
)
|
|
for i, di = range d {
|
|
if len(di) != 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Set k to max(k,i).
|
|
if i > k {
|
|
k = i
|
|
s = append(s, make([]int, k-len(s)+1)...)
|
|
}
|
|
|
|
// Select a vertex v from D[i]. Add v to the
|
|
// beginning of L and remove it from D[i].
|
|
var v graph.Node
|
|
v, d[i] = di[len(di)-1], di[:len(di)-1]
|
|
l = append(l, v)
|
|
s[k]++
|
|
delete(dv, v.ID())
|
|
|
|
// For each neighbor w of v not already in L,
|
|
// subtract one from d_w and move w to the
|
|
// cell of D corresponding to the new value of d_w.
|
|
for _, w := range neighbours[v.ID()] {
|
|
dw, ok := dv[w.ID()]
|
|
if !ok {
|
|
continue
|
|
}
|
|
for i, n := range d[dw] {
|
|
if n.ID() == w.ID() {
|
|
d[dw][i], d[dw] = d[dw][len(d[dw])-1], d[dw][:len(d[dw])-1]
|
|
dw--
|
|
d[dw] = append(d[dw], w)
|
|
break
|
|
}
|
|
}
|
|
dv[w.ID()] = dw
|
|
}
|
|
}
|
|
|
|
return l, s
|
|
}
|
|
|
|
// BronKerbosch returns the set of maximal cliques of the undirected graph g.
|
|
func BronKerbosch(g graph.Undirected) [][]graph.Node {
|
|
nodes := graph.NodesOf(g.Nodes())
|
|
|
|
// The algorithm used here is essentially BronKerbosch3 as described at
|
|
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
|
|
|
|
p := make(set.Nodes, len(nodes))
|
|
for _, n := range nodes {
|
|
p.Add(n)
|
|
}
|
|
x := make(set.Nodes)
|
|
var bk bronKerbosch
|
|
order, _ := degeneracyOrdering(g)
|
|
ordered.Reverse(order)
|
|
for _, v := range order {
|
|
neighbours := graph.NodesOf(g.From(v.ID()))
|
|
nv := make(set.Nodes, len(neighbours))
|
|
for _, n := range neighbours {
|
|
nv.Add(n)
|
|
}
|
|
bk.maximalCliquePivot(g, []graph.Node{v}, make(set.Nodes).Intersect(p, nv), make(set.Nodes).Intersect(x, nv))
|
|
p.Remove(v)
|
|
x.Add(v)
|
|
}
|
|
return bk
|
|
}
|
|
|
|
type bronKerbosch [][]graph.Node
|
|
|
|
func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p, x set.Nodes) {
|
|
if len(p) == 0 && len(x) == 0 {
|
|
*bk = append(*bk, r)
|
|
return
|
|
}
|
|
|
|
neighbours := bk.choosePivotFrom(g, p, x)
|
|
nu := make(set.Nodes, len(neighbours))
|
|
for _, n := range neighbours {
|
|
nu.Add(n)
|
|
}
|
|
for _, v := range p {
|
|
if nu.Has(v) {
|
|
continue
|
|
}
|
|
vid := v.ID()
|
|
neighbours := graph.NodesOf(g.From(vid))
|
|
nv := make(set.Nodes, len(neighbours))
|
|
for _, n := range neighbours {
|
|
nv.Add(n)
|
|
}
|
|
|
|
var found bool
|
|
for _, n := range r {
|
|
if n.ID() == vid {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
var sr []graph.Node
|
|
if !found {
|
|
sr = append(r[:len(r):len(r)], v)
|
|
}
|
|
|
|
bk.maximalCliquePivot(g, sr, make(set.Nodes).Intersect(p, nv), make(set.Nodes).Intersect(x, nv))
|
|
p.Remove(v)
|
|
x.Add(v)
|
|
}
|
|
}
|
|
|
|
func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighbors []graph.Node) {
|
|
// TODO(kortschak): Investigate the impact of pivot choice that maximises
|
|
// |p ⋂ neighbours(u)| as a function of input size. Until then, leave as
|
|
// compile time option.
|
|
if !tomitaTanakaTakahashi {
|
|
for _, n := range p {
|
|
return graph.NodesOf(g.From(n.ID()))
|
|
}
|
|
for _, n := range x {
|
|
return graph.NodesOf(g.From(n.ID()))
|
|
}
|
|
panic("bronKerbosch: empty set")
|
|
}
|
|
|
|
var (
|
|
max = -1
|
|
pivot graph.Node
|
|
)
|
|
maxNeighbors := func(s set.Nodes) {
|
|
outer:
|
|
for _, u := range s {
|
|
nb := graph.NodesOf(g.From(u.ID()))
|
|
c := len(nb)
|
|
if c <= max {
|
|
continue
|
|
}
|
|
for n := range nb {
|
|
if _, ok := p[int64(n)]; ok {
|
|
continue
|
|
}
|
|
c--
|
|
if c <= max {
|
|
continue outer
|
|
}
|
|
}
|
|
max = c
|
|
pivot = u
|
|
neighbors = nb
|
|
}
|
|
}
|
|
maxNeighbors(p)
|
|
maxNeighbors(x)
|
|
if pivot == nil {
|
|
panic("bronKerbosch: empty set")
|
|
}
|
|
return neighbors
|
|
}
|