github.com/gopherd/gonum@v0.0.4/graph/coloring/coloring.go (about) 1 // Copyright ©2021 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 // See Lewis, A Guide to Graph Colouring: Algorithms and Applications 6 // doi:10.1007/978-3-319-25730-3 for significant discussion of approaches. 7 8 package coloring 9 10 import ( 11 "errors" 12 "sort" 13 14 "math/rand" 15 16 "github.com/gopherd/gonum/graph" 17 "github.com/gopherd/gonum/graph/internal/set" 18 "github.com/gopherd/gonum/graph/iterator" 19 "github.com/gopherd/gonum/graph/topo" 20 ) 21 22 // ErrInvalidPartialColoring is returned when a partial coloring 23 // is provided for a graph with inadmissible color assignments. 24 var ErrInvalidPartialColoring = errors.New("coloring: invalid partial coloring") 25 26 // Sets returns the mapping from colors to sets of node IDs. Each set of 27 // node IDs is sorted by ascending value. 28 func Sets(colors map[int64]int) map[int][]int64 { 29 sets := make(map[int][]int64) 30 for id, c := range colors { 31 sets[c] = append(sets[c], id) 32 } 33 for _, s := range sets { 34 sort.Slice(s, func(i, j int) bool { return s[i] < s[j] }) 35 } 36 return sets 37 } 38 39 // Dsatur returns an approximate minimal chromatic number of g and a 40 // corresponding vertex coloring using the heuristic Dsatur coloring algorithm. 41 // If a partial coloring is provided the coloring will be consistent with 42 // that partial coloring if possible. Otherwise Dsatur will return 43 // ErrInvalidPartialColoring. 44 // See Brélaz doi:10.1145/359094.359101 for details of the algorithm. 45 func Dsatur(g graph.Undirected, partial map[int64]int) (k int, colors map[int64]int, err error) { 46 nodes := g.Nodes() 47 n := nodes.Len() 48 if n == 0 { 49 return 50 } 51 partial, ok := newPartial(partial, g) 52 if !ok { 53 return -1, nil, ErrInvalidPartialColoring 54 } 55 order := bySaturationDegree(nodes, g, partial) 56 order.heuristic = order.dsatur 57 k, colors = greedyColoringOf(g, order, order.colors) 58 return k, colors, nil 59 } 60 61 // Terminator is a cancellation-only context type. A context.Context 62 // may be used as a Terminator. 63 type Terminator interface { 64 // Done returns a channel that is closed when work 65 // should be terminated. Done may return nil if this 66 // work can never be canceled. 67 // Successive calls to Done should return the same value. 68 Done() <-chan struct{} 69 70 // If Done is not yet closed, Err returns nil. 71 // If Done is closed, Err returns a non-nil error 72 // explaining why. 73 // After Err returns a non-nil error, successive 74 // calls to Err should return the same error. 75 Err() error 76 } 77 78 // DsaturExact returns the exact minimal chromatic number of g and a 79 // corresponding vertex coloring using the branch-and-bound Dsatur coloring 80 // algorithm of Brélaz. If the provided terminator is cancelled or times out 81 // before completion, the terminator's reason for termination will be returned 82 // along with a potentially sub-optimal chromatic number and coloring. If 83 // term is nil, DsaturExact will run to completion. 84 // See Brélaz doi:10.1145/359094.359101 for details of the algorithm. 85 func DsaturExact(term Terminator, g graph.Undirected) (k int, colors map[int64]int, err error) { 86 // This is implemented essentially as described in algorithm 1 of 87 // doi:10.1002/net.21716 with the exception that we obtain a 88 // tighter upper bound by doing a single run of an approximate 89 // Brélaz Dsatur coloring, using the result if the recurrence is 90 // cancelled. 91 // We also use the initial maximum clique as a starting point for 92 // the exact search. If there is more than one maxumum clique, we 93 // need to ensure that we pick the one that will lead us down the 94 // easiest branch of the search tree. This will be the maximum 95 // clique with the lowest degree into the remainder of the graph. 96 97 nodes := g.Nodes() 98 n := nodes.Len() 99 if n == 0 { 100 return 101 } 102 103 lb, maxClique, cliques := maximumClique(g) 104 if lb == n { 105 return lb, colorClique(maxClique), nil 106 } 107 108 order := bySaturationDegree(nodes, g, make(map[int64]int)) 109 order.heuristic = order.dsatur 110 // Find initial coloring via Dsatur heuristic. 111 ub, initial := greedyColoringOf(g, order, order.colors) 112 if lb == ub { 113 return ub, initial, nil 114 } 115 116 selector := &order.saturationDegree 117 cand := newDsaturColoring(order.nodes, bestMaximumClique(g, cliques)) 118 k, colors, err = dSaturExact(term, selector, cand, len(cand.colors), ub, nil) 119 if colors == nil { 120 return ub, initial, err 121 } 122 if k == lb { 123 err = nil 124 } 125 return k, colors, err 126 } 127 128 // dSaturColoring is a partial graph coloring. 129 type dSaturColoring struct { 130 colors map[int64]int 131 uncolored set.Int64s 132 } 133 134 // newDsaturColoring returns a dSaturColoring representing a partial coloring 135 // of a graph with the given nodes and colors. 136 func newDsaturColoring(nodes []graph.Node, colors map[int64]int) dSaturColoring { 137 uncolored := make(set.Int64s) 138 for _, v := range nodes { 139 vid := v.ID() 140 if _, ok := colors[vid]; !ok { 141 uncolored.Add(vid) 142 } 143 } 144 return dSaturColoring{ 145 colors: colors, 146 uncolored: uncolored, 147 } 148 } 149 150 // color moves a node from the uncolored set to the colored set. 151 func (c dSaturColoring) color(id int64) { 152 if !c.uncolored.Has(id) { 153 if _, ok := c.colors[id]; ok { 154 panic("coloring: coloring already colored node") 155 } 156 panic("coloring: coloring non-existent node") 157 } 158 // The node has its uncolored mark removed, but is 159 // not explicitly colored until the dSaturExact 160 // caller has completed its recursive exploration 161 // of the feasible colors. 162 c.uncolored.Remove(id) 163 } 164 165 // uncolor moves a node from the colored set to the uncolored set. 166 func (c dSaturColoring) uncolor(id int64) { 167 if _, ok := c.colors[id]; !ok { 168 if c.uncolored.Has(id) { 169 panic("coloring: uncoloring already uncolored node") 170 } 171 panic("coloring: uncoloring non-existent node") 172 } 173 delete(c.colors, id) 174 c.uncolored.Add(id) 175 } 176 177 // dSaturExact recursively searches for an exact mimimum vertex coloring of the 178 // full graph in cand. If no chromatic number lower than ub is found, colors is 179 // returned as nil. 180 func dSaturExact(term Terminator, selector *saturationDegree, cand dSaturColoring, k, ub int, best map[int64]int) (newK int, colors map[int64]int, err error) { 181 if len(cand.uncolored) == 0 { 182 // In the published algorithm, this is guarded by k < ub, 183 // but dSaturExact is never called with k >= ub; in the 184 // initial call we have excluded cases where k == ub and 185 // it cannot be greater, and in the recursive call, we 186 // have already checked that k < ub. 187 return k, clone(cand.colors), nil 188 } 189 190 if term != nil { 191 select { 192 case <-term.Done(): 193 if best == nil { 194 return -1, nil, term.Err() 195 } 196 colors := make(set.Ints) 197 for _, c := range best { 198 colors.Add(c) 199 } 200 return colors.Count(), best, term.Err() 201 default: 202 } 203 } 204 205 // Select the next node. 206 selector.reset(cand.colors) 207 vid := selector.nodes[selector.dsatur()].ID() 208 cand.color(vid) 209 // If uncolor panics, we have failed to find a 210 // feasible color. This should never happen. 211 defer cand.uncolor(vid) 212 213 // Keep the adjacent colors set as it will be 214 // overwritten by child recurrences. 215 adjColors := selector.adjColors[selector.indexOf[vid]] 216 217 // Collect all feasible existing colors plus one, remembering it. 218 feasible := make(set.Ints) 219 for _, c := range cand.colors { 220 if adjColors.Has(c) { 221 continue 222 } 223 feasible.Add(c) 224 } 225 var newCol int 226 for c := 0; c < ub; c++ { 227 if feasible.Has(c) || adjColors.Has(c) { 228 continue 229 } 230 feasible.Add(c) 231 newCol = c 232 break 233 } 234 235 // Recur over every feasible color. 236 for c := range feasible { 237 cand.colors[vid] = c 238 effK := k 239 if c == newCol { 240 effK++ 241 } 242 // In the published algorithm, the expression max(effK, lb) < ub is 243 // used, but lb < ub always since it is not updated and dSaturExact 244 // is not called if lb == ub, and it cannot be greater. 245 if effK < ub { 246 ub, best, err = dSaturExact(term, selector, cand, effK, ub, best) 247 if err != nil { 248 return ub, best, err 249 } 250 } 251 } 252 253 return ub, best, nil 254 } 255 256 // maximumClique returns a maximum clique in g and its order. 257 func maximumClique(g graph.Undirected) (k int, maxClique []graph.Node, cliques [][]graph.Node) { 258 cliques = topo.BronKerbosch(g) 259 for _, c := range topo.BronKerbosch(g) { 260 if len(c) > len(maxClique) { 261 maxClique = c 262 } 263 } 264 return len(maxClique), maxClique, cliques 265 } 266 267 // bestMaximumClique returns the maximum clique in g with the lowest degree into 268 // the remainder of the graph. 269 func bestMaximumClique(g graph.Undirected, cliques [][]graph.Node) (colors map[int64]int) { 270 switch len(cliques) { 271 case 0: 272 return nil 273 case 1: 274 return colorClique(cliques[0]) 275 } 276 277 sort.Slice(cliques, func(i, j int) bool { return len(cliques[i]) > len(cliques[j]) }) 278 maxClique := cliques[0] 279 minDegree := cliqueDegree(g, maxClique) 280 for _, c := range cliques[1:] { 281 if len(c) < len(maxClique) { 282 break 283 } 284 d := cliqueDegree(g, c) 285 if d < minDegree { 286 minDegree = d 287 maxClique = c 288 } 289 } 290 291 return colorClique(maxClique) 292 } 293 294 // cliqueDegree returns the degree of the clique to nodes outside the clique. 295 func cliqueDegree(g graph.Undirected, clique []graph.Node) int { 296 n := make(set.Int64s) 297 for _, u := range clique { 298 to := g.From(u.ID()) 299 for to.Next() { 300 n.Add(to.Node().ID()) 301 } 302 } 303 return n.Count() - len(clique) 304 } 305 306 // colorClique returns a valid coloring for the given clique. 307 func colorClique(clique []graph.Node) map[int64]int { 308 colors := make(map[int64]int, len(clique)) 309 for c, u := range clique { 310 colors[u.ID()] = c 311 } 312 return colors 313 } 314 315 // Randomized returns an approximate minimal chromatic number of g and a 316 // corresponding vertex coloring using a greedy coloring algorithm with a 317 // random node ordering. If src is non-nil it will be used as the random 318 // source, otherwise the global random source will be used. If a partial 319 // coloring is provided the coloring will be consistent with that partial 320 // coloring if possible. Otherwise Randomized will return 321 // ErrInvalidPartialColoring. 322 func Randomized(g graph.Undirected, partial map[int64]int, src rand.Source) (k int, colors map[int64]int, err error) { 323 nodes := g.Nodes() 324 n := nodes.Len() 325 if n == 0 { 326 return 327 } 328 partial, ok := newPartial(partial, g) 329 if !ok { 330 return -1, nil, ErrInvalidPartialColoring 331 } 332 k, colors = greedyColoringOf(g, randomize(nodes, src), partial) 333 return k, colors, nil 334 } 335 336 // randomize returns a graph.Node iterator that returns nodes in a random 337 // order. 338 func randomize(it graph.Nodes, src rand.Source) graph.Nodes { 339 nodes := graph.NodesOf(it) 340 var shuffle func(int, func(i, j int)) 341 if src == nil { 342 shuffle = rand.Shuffle 343 } else { 344 shuffle = rand.New(src).Shuffle 345 } 346 shuffle(len(nodes), func(i, j int) { 347 nodes[i], nodes[j] = nodes[j], nodes[i] 348 }) 349 return iterator.NewOrderedNodes(nodes) 350 } 351 352 // RecursiveLargestFirst returns an approximate minimal chromatic number 353 // of g and a corresponding vertex coloring using the Recursive Largest 354 // First coloring algorithm. 355 // See Leighton doi:10.6028/jres.084.024 for details of the algorithm. 356 func RecursiveLargestFirst(g graph.Undirected) (k int, colors map[int64]int) { 357 it := g.Nodes() 358 n := it.Len() 359 if n == 0 { 360 return 361 } 362 nodes := graph.NodesOf(it) 363 colors = make(map[int64]int) 364 365 // The names of variable here have been changed from the original PL-1 366 // for clarity, but the correspondence is as follows: 367 // E -> isolated 368 // F -> boundary 369 // L -> current 370 // COL -> k 371 // C -> colors 372 373 // Initialize the boundary vector to the node degrees. 374 boundary := make([]int, len(nodes)) 375 indexOf := make(map[int64]int) 376 for i, u := range nodes { 377 uid := u.ID() 378 indexOf[uid] = i 379 boundary[i] = g.From(uid).Len() 380 } 381 deleteFrom := func(vec []int, idx int) { 382 vec[idx] = -1 383 to := g.From(nodes[idx].ID()) 384 for to.Next() { 385 vec[indexOf[to.Node().ID()]]-- 386 } 387 } 388 isolated := make([]int, len(nodes)) 389 390 // If there are any uncolored nodes, initiate the assignment of the next color. 391 // Incrementing color happens at the end of the loop in this implementation. 392 var current int 393 for j := 0; j < n; { 394 // Reinitialize the isolated vector. 395 copy(isolated, boundary) 396 397 // Select the node in U₁ with maximal degree in U₁. 398 for i := range nodes { 399 if boundary[i] > boundary[current] { 400 current = i 401 } 402 } 403 404 // Color the node just selected and continue to 405 // color nodes with color k until U₁ is empty. 406 for isolated[current] >= 0 { 407 // Color node and modify U₁ and U₂ accordingly. 408 deleteFrom(isolated, current) 409 deleteFrom(boundary, current) 410 colors[nodes[current].ID()] = k 411 j++ 412 to := g.From(nodes[current].ID()) 413 for to.Next() { 414 i := indexOf[to.Node().ID()] 415 if isolated[i] >= 0 { 416 deleteFrom(isolated, i) 417 } 418 } 419 420 // Find the first node in U₁, if any. 421 for i := range nodes { 422 if isolated[i] < 0 { 423 continue 424 } 425 426 // If U₁ is not empty, select the next node for coloring. 427 current = i 428 currAvail := boundary[current] - isolated[current] 429 for j := i; j < n; j++ { 430 if isolated[j] < 0 { 431 continue 432 } 433 nextAvail := boundary[j] - isolated[j] 434 switch { 435 case nextAvail > currAvail, nextAvail == currAvail && isolated[j] < isolated[current]: 436 current = j 437 currAvail = boundary[current] - isolated[current] 438 } 439 } 440 break 441 } 442 } 443 444 k++ 445 } 446 447 return k, colors 448 } 449 450 // SanSegundo returns an approximate minimal chromatic number of g and a 451 // corresponding vertex coloring using the PASS rule with a single run of a 452 // greedy coloring algorithm. If a partial coloring is provided the coloring 453 // will be consistent with that partial coloring if possible. Otherwise 454 // SanSegundo will return ErrInvalidPartialColoring. 455 // See San Segundo doi:10.1016/j.cor.2011.10.008 for details of the algorithm. 456 func SanSegundo(g graph.Undirected, partial map[int64]int) (k int, colors map[int64]int, err error) { 457 nodes := g.Nodes() 458 n := nodes.Len() 459 if n == 0 { 460 return 461 } 462 partial, ok := newPartial(partial, g) 463 if !ok { 464 return -1, nil, ErrInvalidPartialColoring 465 } 466 order := bySaturationDegree(nodes, g, partial) 467 order.heuristic = order.pass 468 k, colors = greedyColoringOf(g, order, order.colors) 469 return k, colors, nil 470 } 471 472 // WelshPowell returns an approximate minimal chromatic number of g and a 473 // corresponding vertex coloring using the Welsh and Powell coloring algorithm. 474 // If a partial coloring is provided the coloring will be consistent with that 475 // partial coloring if possible. Otherwise WelshPowell will return 476 // ErrInvalidPartialColoring. 477 // See Welsh and Powell doi:10.1093/comjnl/10.1.85 for details of the algorithm. 478 func WelshPowell(g graph.Undirected, partial map[int64]int) (k int, colors map[int64]int, err error) { 479 nodes := g.Nodes() 480 n := nodes.Len() 481 if n == 0 { 482 return 483 } 484 partial, ok := newPartial(partial, g) 485 if !ok { 486 return -1, nil, ErrInvalidPartialColoring 487 } 488 k, colors = greedyColoringOf(g, byDescendingDegree(nodes, g), partial) 489 return k, colors, nil 490 } 491 492 // byDescendingDegree returns a graph.Node iterator that returns nodes 493 // in order of descending degree. 494 func byDescendingDegree(it graph.Nodes, g graph.Undirected) graph.Nodes { 495 nodes := graph.NodesOf(it) 496 n := byDescDegree{nodes: nodes, degrees: make([]int, len(nodes))} 497 for i, u := range nodes { 498 n.degrees[i] = g.From(u.ID()).Len() 499 } 500 sort.Sort(n) 501 return iterator.NewOrderedNodes(nodes) 502 } 503 504 // byDescDegree sorts a slice of graph.Node descending by the corresponding 505 // value of the degrees slice. 506 type byDescDegree struct { 507 nodes []graph.Node 508 degrees []int 509 } 510 511 func (n byDescDegree) Len() int { return len(n.nodes) } 512 func (n byDescDegree) Less(i, j int) bool { return n.degrees[i] > n.degrees[j] } 513 func (n byDescDegree) Swap(i, j int) { 514 n.nodes[i], n.nodes[j] = n.nodes[j], n.nodes[i] 515 n.degrees[i], n.degrees[j] = n.degrees[j], n.degrees[i] 516 } 517 518 // newPartial returns a new valid partial coloring is valid for g. An empty 519 // partial coloring is valid. If the partial coloring is not valid, a nil map 520 // is returned, otherwise a new non-nil map is returned. If the input partial 521 // coloring is nil, a new map is created and returned. 522 func newPartial(partial map[int64]int, g graph.Undirected) (map[int64]int, bool) { 523 if partial == nil { 524 return make(map[int64]int), true 525 } 526 for id, c := range partial { 527 if g.Node(id) == nil { 528 return nil, false 529 } 530 to := g.From(id) 531 for to.Next() { 532 if oc, ok := partial[to.Node().ID()]; ok && c == oc { 533 return nil, false 534 } 535 } 536 } 537 return clone(partial), true 538 } 539 540 func clone(colors map[int64]int) map[int64]int { 541 new := make(map[int64]int, len(colors)) 542 for id, c := range colors { 543 new[id] = c 544 } 545 return new 546 } 547 548 // greedyColoringOf returns the chromatic number and a graph coloring of g 549 // based on the sequential coloring of nodes given by order and starting from 550 // the given partial coloring. 551 func greedyColoringOf(g graph.Undirected, order graph.Nodes, partial map[int64]int) (k int, colors map[int64]int) { 552 colors = partial 553 constrained := false 554 for _, c := range colors { 555 if c > k { 556 k = c 557 constrained = true 558 } 559 } 560 561 // Next nodes are chosen by the specified heuristic in order. 562 for order.Next() { 563 uid := order.Node().ID() 564 used := colorsOf(g.From(uid), colors) 565 if c, ok := colors[uid]; ok { 566 if used.Has(c) { 567 return -1, nil 568 } 569 continue 570 } 571 // Color the chosen vertex with the least possible 572 // (lowest numbered) color. 573 for c := 0; c <= k+1; c++ { 574 if !used.Has(c) { 575 colors[uid] = c 576 if c > k { 577 k = c 578 } 579 break 580 } 581 } 582 } 583 584 if !constrained { 585 return k + 1, colors 586 } 587 seen := make(set.Ints) 588 for _, c := range colors { 589 seen.Add(c) 590 } 591 return seen.Count(), colors 592 } 593 594 // colorsOf returns all the colors in the coloring that are used by the 595 // given nodes. 596 func colorsOf(nodes graph.Nodes, coloring map[int64]int) set.Ints { 597 c := make(set.Ints, nodes.Len()) 598 for nodes.Next() { 599 used, ok := coloring[nodes.Node().ID()] 600 if ok { 601 c.Add(used) 602 } 603 } 604 return c 605 } 606 607 // saturationDegreeIterator is a graph.Nodes iterator that returns nodes ordered 608 // by decreasing saturation degree. 609 type saturationDegreeIterator struct { 610 // cnt is the number of nodes that 611 // have been returned and curr is 612 // the current selection. 613 cnt, curr int 614 615 // heuristic determines the 616 // iterator's node selection 617 // heuristic. It can be either 618 // saturationDegree.dsatur or 619 // saturationDegree.pass. 620 heuristic func() int 621 622 saturationDegree 623 } 624 625 // bySaturationDegree returns a new saturationDegreeIterator that will 626 // iterate over the node in it based on the given graph and partial coloring. 627 // The saturationDegreeIterator holds a reference to colors allowing 628 // greedyColoringOf to update its coloring. 629 func bySaturationDegree(it graph.Nodes, g graph.Undirected, colors map[int64]int) *saturationDegreeIterator { 630 return &saturationDegreeIterator{ 631 cnt: -1, curr: -1, 632 saturationDegree: newSaturationDegree(it, g, colors), 633 } 634 } 635 636 // Len returns the number of elements remaining in the iterator. 637 // saturationDegreeIterator is an indeterminate iterator, so Len always 638 // returns -1. This is required to satisfy the graph.Iterator interface. 639 func (n *saturationDegreeIterator) Len() int { return -1 } 640 641 // Next advances the iterator to the next node and returns whether 642 // the next call to the Node method will return a valid Node. 643 func (n *saturationDegreeIterator) Next() bool { 644 if uint(n.cnt)+1 < uint(len(n.nodes)) { 645 n.cnt++ 646 switch n.cnt { 647 case 0: 648 max := -1 649 for i, d := range n.degrees { 650 if d > max { 651 max = d 652 n.curr = i 653 } 654 } 655 default: 656 prev := n.Node().ID() 657 c := n.colors[prev] 658 to := n.g.From(prev) 659 for to.Next() { 660 n.adjColors[n.indexOf[to.Node().ID()]].Add(c) 661 } 662 663 chosen := n.heuristic() 664 if chosen < 0 || chosen == n.curr { 665 return false 666 } 667 n.curr = chosen 668 } 669 return true 670 } 671 n.cnt = len(n.nodes) 672 return false 673 } 674 675 // Node returns the current node. 676 func (n *saturationDegreeIterator) Node() graph.Node { return n.nodes[n.curr] } 677 678 // Reset implements the graph.Iterator interface. It should not be called. 679 func (n *saturationDegreeIterator) Reset() { panic("coloring: invalid call to Reset") } 680 681 // saturationDegree is a saturation degree node choice heuristic. 682 type saturationDegree struct { 683 // nodes is the set of nodes being 684 // iterated over. 685 nodes []graph.Node 686 687 // indexOf is a mapping between node 688 // IDs and elements of degree and 689 // adjColors. degrees holds the 690 // degree of each node and adjColors 691 // holds the current adjacent 692 // colors of each node. 693 indexOf map[int64]int 694 degrees []int 695 adjColors []set.Ints 696 697 // g and colors are the graph coloring. 698 // colors is held by both the iterator 699 // and greedyColoringOf. 700 g graph.Undirected 701 colors map[int64]int 702 703 // work is a temporary workspace. 704 work []int 705 } 706 707 // newSaturationDegree returns a saturationDegree heuristic based on the 708 // nodes in the given node iterator and graph, using the provided coloring. 709 func newSaturationDegree(it graph.Nodes, g graph.Undirected, colors map[int64]int) saturationDegree { 710 nodes := graph.NodesOf(it) 711 sd := saturationDegree{ 712 nodes: nodes, 713 indexOf: make(map[int64]int, len(nodes)), 714 degrees: make([]int, len(nodes)), 715 adjColors: make([]set.Ints, len(nodes)), 716 g: g, 717 colors: colors, 718 } 719 for i, u := range nodes { 720 sd.degrees[i] = g.From(u.ID()).Len() 721 sd.adjColors[i] = make(set.Ints) 722 sd.indexOf[u.ID()] = i 723 } 724 for uid, c := range colors { 725 to := g.From(uid) 726 for to.Next() { 727 sd.adjColors[sd.indexOf[to.Node().ID()]].Add(c) 728 } 729 } 730 return sd 731 } 732 733 // reset re-initializes the saturation with the provided colors. 734 func (sd *saturationDegree) reset(colors map[int64]int) { 735 sd.colors = colors 736 for i := range sd.nodes { 737 sd.adjColors[i] = make(set.Ints) 738 } 739 for uid, c := range colors { 740 to := sd.g.From(uid) 741 for to.Next() { 742 sd.adjColors[sd.indexOf[to.Node().ID()]].Add(c) 743 } 744 } 745 } 746 747 // dsatur implements the Dsatur heuristic from Brélaz doi:10.1145/359094.359101. 748 func (sd *saturationDegree) dsatur() int { 749 maxSat, maxDeg, chosen := -1, -1, -1 750 for i, u := range sd.nodes { 751 uid := u.ID() 752 if _, ok := sd.colors[uid]; ok { 753 continue 754 } 755 s := saturationDegreeOf(uid, sd.g, sd.colors) 756 d := sd.degrees[i] 757 switch { 758 // Choose a vertex with a maximal saturation degree. 759 // If there is an equality, choose any vertex of maximal 760 // degree in the uncolored subgraph. 761 case s > maxSat, s == maxSat && d > maxDeg: 762 maxSat = s 763 maxDeg = d 764 chosen = i 765 } 766 } 767 return chosen 768 } 769 770 // pass implements the PASS heuristic from San Segundo doi:10.1016/j.cor.2011.10.008. 771 func (sd *saturationDegree) pass() int { 772 maxSat, chosen := -1, -1 773 sd.work = sd.work[:0] 774 for i, u := range sd.nodes { 775 uid := u.ID() 776 if _, ok := sd.colors[uid]; ok { 777 continue 778 } 779 s := saturationDegreeOf(uid, sd.g, sd.colors) 780 switch { 781 case s > maxSat: 782 maxSat = s 783 sd.work = sd.work[:0] 784 fallthrough 785 case s == maxSat: 786 sd.work = append(sd.work, i) 787 } 788 } 789 maxAvail := -1 790 for _, vs := range sd.work { 791 var avail int 792 for _, v := range sd.work { 793 if v != vs { 794 avail += sd.same(sd.adjColors[vs], sd.adjColors[v]) 795 } 796 } 797 switch { 798 case avail > maxAvail, avail == maxAvail && sd.nodes[chosen].ID() < sd.nodes[vs].ID(): 799 maxAvail = avail 800 chosen = vs 801 } 802 } 803 return chosen 804 } 805 806 // same implements the same function from San Segundo doi:10.1016/j.cor.2011.10.008. 807 func (sd *saturationDegree) same(vi, vj set.Ints) int { 808 valid := make(set.Ints) 809 for _, c := range sd.colors { 810 if !vi.Has(c) { 811 valid.Add(c) 812 } 813 } 814 for c := range vj { 815 valid.Remove(c) 816 } 817 return valid.Count() 818 } 819 820 // saturationDegreeOf returns the saturation degree of the node corresponding to 821 // vid in g with the given coloring. 822 func saturationDegreeOf(vid int64, g graph.Undirected, colors map[int64]int) int { 823 if _, ok := colors[vid]; ok { 824 panic("coloring: saturation degree not defined for colored node") 825 } 826 adjColors := make(set.Ints) 827 to := g.From(vid) 828 for to.Next() { 829 if c, ok := colors[to.Node().ID()]; ok { 830 adjColors.Add(c) 831 } 832 } 833 return adjColors.Count() 834 }