gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/graph/topo/bron_kerbosch.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 topo 6 7 import ( 8 "slices" 9 10 "gonum.org/v1/gonum/graph" 11 "gonum.org/v1/gonum/graph/internal/set" 12 ) 13 14 // DegeneracyOrdering returns the degeneracy ordering and the k-cores of 15 // the undirected graph g. 16 func DegeneracyOrdering(g graph.Undirected) (order []graph.Node, cores [][]graph.Node) { 17 order, offsets := degeneracyOrdering(g) 18 19 slices.Reverse(order) 20 cores = make([][]graph.Node, len(offsets)) 21 offset := len(order) 22 for i, n := range offsets { 23 cores[i] = order[offset-n : offset] 24 offset -= n 25 } 26 return order, cores 27 } 28 29 // KCore returns the k-core of the undirected graph g with nodes in an 30 // optimal ordering for the coloring number. 31 func KCore(k int, g graph.Undirected) []graph.Node { 32 order, offsets := degeneracyOrdering(g) 33 34 var offset int 35 for _, n := range offsets[:k] { 36 offset += n 37 } 38 core := make([]graph.Node, len(order)-offset) 39 copy(core, order[offset:]) 40 return core 41 } 42 43 // degeneracyOrdering is the common code for DegeneracyOrdering and KCore. It 44 // returns l, the nodes of g in optimal ordering for coloring number and 45 // s, a set of relative offsets into l for each k-core, where k is an index 46 // into s. 47 func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) { 48 nodes := graph.NodesOf(g.Nodes()) 49 50 // The algorithm used here is essentially as described at 51 // http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710 52 53 // Initialize an output list L in return parameters. 54 55 // Compute a number d_v for each vertex v in G, 56 // the number of neighbors of v that are not already in L. 57 // Initially, these numbers are just the degrees of the vertices. 58 dv := make(map[int64]int, len(nodes)) 59 var ( 60 maxDegree int 61 neighbours = make(map[int64][]graph.Node) 62 ) 63 for _, n := range nodes { 64 id := n.ID() 65 adj := graph.NodesOf(g.From(id)) 66 neighbours[id] = adj 67 dv[id] = len(adj) 68 if len(adj) > maxDegree { 69 maxDegree = len(adj) 70 } 71 } 72 73 // Initialize an array D such that D[i] contains a list of the 74 // vertices v that are not already in L for which d_v = i. 75 d := make([][]graph.Node, maxDegree+1) 76 for _, n := range nodes { 77 deg := dv[n.ID()] 78 d[deg] = append(d[deg], n) 79 } 80 81 // Initialize k to 0. 82 k := 0 83 // Repeat n times: 84 s = []int{0} 85 for range nodes { 86 // Scan the array cells D[0], D[1], ... until 87 // finding an i for which D[i] is nonempty. 88 var ( 89 i int 90 di []graph.Node 91 ) 92 for i, di = range d { 93 if len(di) != 0 { 94 break 95 } 96 } 97 98 // Set k to max(k,i). 99 if i > k { 100 k = i 101 s = append(s, make([]int, k-len(s)+1)...) 102 } 103 104 // Select a vertex v from D[i]. Add v to the 105 // beginning of L and remove it from D[i]. 106 var v graph.Node 107 v, d[i] = di[len(di)-1], di[:len(di)-1] 108 l = append(l, v) 109 s[k]++ 110 delete(dv, v.ID()) 111 112 // For each neighbor w of v not already in L, 113 // subtract one from d_w and move w to the 114 // cell of D corresponding to the new value of d_w. 115 for _, w := range neighbours[v.ID()] { 116 dw, ok := dv[w.ID()] 117 if !ok { 118 continue 119 } 120 for i, n := range d[dw] { 121 if n.ID() == w.ID() { 122 d[dw][i], d[dw] = d[dw][len(d[dw])-1], d[dw][:len(d[dw])-1] 123 dw-- 124 d[dw] = append(d[dw], w) 125 break 126 } 127 } 128 dv[w.ID()] = dw 129 } 130 } 131 132 return l, s 133 } 134 135 // BronKerbosch returns the set of maximal cliques of the undirected graph g. 136 func BronKerbosch(g graph.Undirected) [][]graph.Node { 137 nodes := graph.NodesOf(g.Nodes()) 138 139 // The algorithm used here is essentially BronKerbosch3 as described at 140 // http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858 141 142 p := set.NewNodesSize(len(nodes)) 143 for _, n := range nodes { 144 p.Add(n) 145 } 146 x := set.NewNodes() 147 var bk bronKerbosch 148 order, _ := degeneracyOrdering(g) 149 slices.Reverse(order) 150 for _, v := range order { 151 neighbours := graph.NodesOf(g.From(v.ID())) 152 nv := set.NewNodesSize(len(neighbours)) 153 for _, n := range neighbours { 154 nv.Add(n) 155 } 156 bk.maximalCliquePivot(g, []graph.Node{v}, set.IntersectionOfNodes(p, nv), set.IntersectionOfNodes(x, nv)) 157 p.Remove(v) 158 x.Add(v) 159 } 160 return bk 161 } 162 163 type bronKerbosch [][]graph.Node 164 165 func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p, x set.Nodes) { 166 if len(p) == 0 && len(x) == 0 { 167 *bk = append(*bk, r) 168 return 169 } 170 171 neighbours := bk.choosePivotFrom(g, p, x) 172 nu := set.NewNodesSize(len(neighbours)) 173 for _, n := range neighbours { 174 nu.Add(n) 175 } 176 for _, v := range p { 177 if nu.Has(v) { 178 continue 179 } 180 vid := v.ID() 181 neighbours := graph.NodesOf(g.From(vid)) 182 nv := set.NewNodesSize(len(neighbours)) 183 for _, n := range neighbours { 184 nv.Add(n) 185 } 186 187 var found bool 188 for _, n := range r { 189 if n.ID() == vid { 190 found = true 191 break 192 } 193 } 194 var sr []graph.Node 195 if !found { 196 sr = append(r[:len(r):len(r)], v) 197 } 198 199 bk.maximalCliquePivot(g, sr, set.IntersectionOfNodes(p, nv), set.IntersectionOfNodes(x, nv)) 200 p.Remove(v) 201 x.Add(v) 202 } 203 } 204 205 func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighbors []graph.Node) { 206 // TODO(kortschak): Investigate the impact of pivot choice that maximises 207 // |p ⋂ neighbours(u)| as a function of input size. Until then, leave as 208 // compile time option. 209 if !tomitaTanakaTakahashi { 210 for _, n := range p { 211 return graph.NodesOf(g.From(n.ID())) 212 } 213 for _, n := range x { 214 return graph.NodesOf(g.From(n.ID())) 215 } 216 panic("bronKerbosch: empty set") 217 } 218 219 var ( 220 max = -1 221 pivot graph.Node 222 ) 223 maxNeighbors := func(s set.Nodes) { 224 outer: 225 for _, u := range s { 226 nb := graph.NodesOf(g.From(u.ID())) 227 c := len(nb) 228 if c <= max { 229 continue 230 } 231 for n := range nb { 232 if _, ok := p[int64(n)]; ok { 233 continue 234 } 235 c-- 236 if c <= max { 237 continue outer 238 } 239 } 240 max = c 241 pivot = u 242 neighbors = nb 243 } 244 } 245 maxNeighbors(p) 246 maxNeighbors(x) 247 if pivot == nil { 248 panic("bronKerbosch: empty set") 249 } 250 return neighbors 251 }