gonum.org/v1/gonum@v0.14.0/graph/coloring/coloring_test.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 package coloring 6 7 import ( 8 "context" 9 "flag" 10 "testing" 11 "time" 12 13 "golang.org/x/exp/rand" 14 15 "gonum.org/v1/gonum/graph" 16 "gonum.org/v1/gonum/graph/encoding/graph6" 17 "gonum.org/v1/gonum/graph/internal/set" 18 "gonum.org/v1/gonum/graph/simple" 19 ) 20 21 var runLong = flag.Bool("color.long", false, "run long exact coloring tests") 22 23 var coloringTests = []struct { 24 name string 25 g graph.Undirected 26 colors int 27 28 long bool 29 30 partial map[int64]int 31 32 dsatur set.Ints 33 randomized set.Ints 34 rlf set.Ints 35 sanSegundo set.Ints 36 welshPowell set.Ints 37 }{ 38 { 39 name: "empty", 40 g: simple.NewUndirectedGraph(), 41 colors: 0, 42 }, 43 { 44 name: "singleton", // https://hog.grinvin.org/ViewGraphInfo.action?id=1310 45 g: graph6.Graph("@"), 46 colors: 1, 47 }, 48 { 49 name: "kite", // https://hog.grinvin.org/ViewGraphInfo.action?id=782 50 g: graph6.Graph("DzC"), 51 colors: 3, 52 randomized: setOf(3, 4), 53 54 partial: map[int64]int{1: 2}, 55 }, 56 { 57 name: "triangle+1", 58 g: graph6.Graph("Cw"), 59 colors: 3, 60 61 partial: map[int64]int{1: 2}, 62 }, 63 { 64 name: "bipartite halves", 65 g: graph6.Graph("G?]uf?"), 66 colors: 2, 67 randomized: setOf(2, 3, 4), 68 69 partial: map[int64]int{1: 3}, 70 }, 71 { 72 name: "bipartite alternating", 73 g: graph6.Graph("GKUalO"), 74 colors: 2, 75 randomized: setOf(2, 3, 4), 76 welshPowell: setOf(2, 3, 4), 77 78 partial: map[int64]int{1: 1}, 79 }, 80 { 81 name: "3/4 bipartite", // https://hog.grinvin.org/ViewGraphInfo.action?id=466 82 g: graph6.Graph("F?~v_"), 83 colors: 2, 84 85 partial: map[int64]int{1: 1}, 86 }, 87 { 88 name: "cubical", // https://hog.grinvin.org/ViewGraphInfo.action?id=1022 89 g: graph6.Graph("Gs@ipo"), 90 colors: 2, 91 randomized: setOf(2, 3, 4), 92 welshPowell: setOf(2, 3), 93 94 partial: map[int64]int{1: 1}, 95 }, 96 { 97 name: "HoG33106", // https://hog.grinvin.org/ViewGraphInfo.action?id=33106 98 g: graph6.Graph("K???WWKKxf]C"), 99 colors: 2, 100 randomized: setOf(2, 3, 4), 101 102 partial: map[int64]int{1: 1}, 103 }, 104 { 105 name: "HoG41237", // https://hog.grinvin.org/ViewGraphInfo.action?id=41237 106 g: graph6.Graph("~?BCs?GO?@?O??@?A????G?A?????G??O????????????????????G????_???O??????????????????????OC????A???????????????@@?????A?????????????????????????C??????G??????O?????????????????????????G?@????????@???????D????????_??????CG???????@???????????????????????ACG???????EA???????O?G??????S????????C???_?????O??C?????@??????????W????????B??????????????O?????G??????????_????o????A????A_???@O_????????@?O????G???W???????????@G?????????A??????@?????S??????????????????????????????B????????????K????????????????????????@_????????????o??G????????????A?????????????_?????????_??G?????????G????@_???????????C?_????????????@????????A???A?????????A???O?????????????C???????????K??_??????????@O??_??????????C??@???????????G??????????E????????A??G??????????????????C????A???@???????????O??O???????????????_????????????@_?_????????????@O?C?????????????_?@?????????????G????C???O???????????????G?O????????????????O???????O???C????????????O?A?????????????????_??????????????@_C???????????????I?C??????????????@??G???????????????_???CA??????????????????_???????O????????O???????????????A??????????????????G?????????????????_C?????????????????OC?????????????????g????????D?????????????????@?G?????????????????GA??????????????????K??????????????????@O???????????????G????????G????????????????????P???????????????????OO???????????????????@_??????????????????????????????????????????????????????????????????@?????????????????????_???A?????????????????????????????????????????????????????????????O?????????????????????_??@????????????????????????????????????????G???????????@????????????????????????????????C?????????????????????????????????????????????????K??????????????????????I??????????????????????C_???????????????????_C????????????????????????K???????????????????????K???????????????????????`???????????????????????`??????????????????????I???????????????????????C?O?????????????????????A?O??????????????????????O?G??????????????????????@?C???????????????????????H????????????????????????OG???????????????????????GC?????????????????????????B?????????????????????????I?????????????????????????????????????????????????????????????????????????????????????????????????????????????c?????????????????????????CO???????????????????????A?A????????????????????????C?@????????????????????????C??_???????????????????????O??O?????????????????????????o???????????????????????????K???????????????????????????E??????????????????????????@_?????????????????????????D????????????????????????????`???????????????????????????????CG??????????????????????????@?O??????????????????????????@O???????????????????????????CG???????????????????????????C@???????????????????????????A?_?????????????????????C?????????????????????????????G?????????????????????????????@????????????????????????????????????AA?????????????????????????????B?????????????????????????????AG?????????????????????????????G_????????????????????????????CA?????????????????????????????CG??????????????????????????A???????????????????????????????O??????????????????????????????G????A?????????????????????????????AAG?????????????????????????????CCC??????????????????????????????cG?????????????????????????????@CA?????????????????????????????AG@??????????????????????????????P?"), 107 colors: 2, 108 randomized: setOf(2, 3, 4), 109 welshPowell: setOf(2, 3), 110 111 partial: map[int64]int{1: 1}, 112 }, 113 114 // Test graphs from Leighton doi:10.6028/jres.084.024. 115 { 116 name: "exams", // Figure 4 induced by the nodes in E. 117 g: graph6.Graph("EDZw"), 118 colors: 3, 119 randomized: setOf(3, 4), 120 121 partial: map[int64]int{1: 2}, 122 }, 123 { 124 name: "exam_scheduler_1", // Figure 4. 125 g: graph6.Graph("JQ?HaWN{l~?"), 126 colors: 5, 127 randomized: setOf(5, 6), 128 }, 129 { 130 name: "exam_scheduler_2", // Figure 11. 131 g: graph6.Graph("GTPHz{"), 132 colors: 4, 133 randomized: setOf(4, 5), 134 135 partial: map[int64]int{1: 3}, 136 }, 137 138 // Test graph from Brélaz doi:10.1145/359094.359101. 139 { 140 name: "Brélaz", // Figure 1. 141 g: graph6.Graph(`I??GG\rmg`), 142 colors: 3, 143 randomized: setOf(3, 4), 144 rlf: setOf(3, 4), 145 welshPowell: setOf(3, 4), 146 147 partial: map[int64]int{1: 2}, 148 }, 149 150 // Test graph from Lima and Carmo doi:10.22456/2175-2745.80721. 151 { 152 name: "Lima Carmo", // Figure 2. 153 g: graph6.Graph("Gh]S?G"), 154 colors: 3, 155 randomized: setOf(3, 4), 156 sanSegundo: setOf(3, 4), 157 158 partial: map[int64]int{1: 2}, 159 }, 160 161 // Test graph from San Segundo doi:10.1016/j.cor.2011.10.008. 162 { 163 name: "San Segundo", // Figure 1 A. 164 g: graph6.Graph(`HMn\r\v`), 165 colors: 5, 166 randomized: setOf(5, 6), 167 168 partial: map[int64]int{1: 4}, 169 }, 170 171 { 172 name: "tetrahedron", // https://hog.grinvin.org/ViewGraphInfo.action?id=74 173 g: graph6.Graph("C~"), 174 colors: 4, 175 176 partial: map[int64]int{1: 3}, 177 }, 178 179 { 180 name: "triangle", // https://hog.grinvin.org/ViewGraphInfo.action?id=1374 181 g: graph6.Graph("Bw"), 182 colors: 3, 183 184 partial: map[int64]int{1: 2}, 185 }, 186 { 187 name: "square", // https://hog.grinvin.org/ViewGraphInfo.action?id=674 188 g: graph6.Graph("Cl"), 189 colors: 2, 190 191 partial: map[int64]int{1: 1}, 192 }, 193 { 194 name: "cycle-5", // https://hog.grinvin.org/ViewGraphInfo.action?id=340 195 g: graph6.Graph("Dhc"), 196 colors: 3, 197 198 partial: map[int64]int{1: 2}, 199 }, 200 { 201 name: "cycle-6", // https://hog.grinvin.org/ViewGraphInfo.action?id=670 202 g: graph6.Graph("EhEG"), 203 colors: 2, 204 randomized: setOf(2, 3), 205 206 partial: map[int64]int{1: 1}, 207 }, 208 209 { 210 name: "wheel-5", // https://hog.grinvin.org/ViewGraphInfo.action?id=442 211 g: graph6.Graph("D|s"), 212 colors: 3, 213 214 partial: map[int64]int{1: 2}, 215 }, 216 { 217 name: "wheel-6", // https://hog.grinvin.org/ViewGraphInfo.action?id=204 218 g: graph6.Graph("E|fG"), 219 colors: 4, 220 221 partial: map[int64]int{1: 3}, 222 }, 223 { 224 name: "wheel-7", 225 g: graph6.Graph("F|eMG"), 226 colors: 3, 227 randomized: setOf(3, 4), 228 229 partial: map[int64]int{1: 2}, 230 }, 231 232 { 233 name: "sudoku board", // The graph of the constraints of a sudoku puzzle board. 234 g: graph6.Graph("~?@P~~~~~~wF?{BFFFFbbwF~?~{B~~?wF?wF_[BF?wwwFFb_[^?wF~?wF~_[B~{?_C?OA?OC_C?_W_C?_wOA?O{C?_C^?_C?fwA?OA~_C?_F~_C?_F?OA?OF?c?_CB_W_C?_FFA?OA?w{C?_CBbwC?_C?~wA?OA?~{?_C?_^~_C?_F?wA?OA?wF?c?_CB_[BC?_C?wFFA?OA?wFF__C?_[BbwC?_C?wF~?OA?OF?~{?_C?_[B~{?_C?_C?_A?OA?OA?OC_C?_C?_CBC?_C?_C?_wOA?OA?OAF__C?_C?_C^?_C?_C?_C~?OA?OA?OA~_C?_C?_C?~{?_C?_C?_F?OA?OA?OA?wC_C?_C?_CB_W_C?_C?_C?wwOA?OA?OA?w{C?_C?_C?_[^?_C?_C?_C?~wA?OA?OA?OF~_C?_C?_C?_^~_C?_C?_C?wF?OA?OA?OA?wF?c?_C?_C?_[B_W_C?_C?_C?wFFA?OA?OA?OF?w{C?_C?_C?_[BbwC?_C?_C?_F?~wA?OA?OA?OF?~{?_C?_C?_CB_^~"), 235 colors: 9, 236 randomized: setOf(9, 10, 11, 12, 13), 237 sanSegundo: setOf(9, 10), 238 welshPowell: setOf(9, 10, 11, 12, 13, 14), 239 240 partial: map[int64]int{1: 3}, 241 }, 242 { 243 name: "sudoku problem", // The constraint graph for the problem in the sudoku example. 244 g: graph6.Graph("~?@Y~~~~~~?F|_B?F?F_Bw?~?F{?^w?w??wC?[Bzwww?FF_?[^??F~??F~~kB~w?wF?v~?wFz{B_W?F?w|~F?w{?B_[^??F?~w??wF~_?B_^~?C?_C??A?OA?_?_C?_W?C?_CF??OA?O~|__C?bw??_C?fw??OA?V~}_C?_F~?C?_C?wD~OA?OF?_?_C?_[B??_C?_FF}wOA?OFF_?C?_CBbw??_C?_F~~oA?OA?~{??C?_CB~~^_C?_F?w??OA?OF?wC?C?_CB_[B|w_C?_F?ww?A?OA?wFF_?C?_CB_[^??C?_C?wF~??A?OA?wF~_??_C?_[B~w?_C?_C?_C??A?OA?OA?OC?C?_C?_C?_W?C?_C?_C?_~}A?OA?OA?O{??_C?_C?_C^vwC?_C?_C?f|~?OA?OA?OA~_??_C?_C?_F~|{?_C?_C?_F??A?OA?OA?OF?_?_C?_C?_CB_W?C?_C?_C?_FF??OA?OA?OA?w|~__C?_C?_CBbw??_C?_C?_C?~w??OA?OA?OA?~{??C?_C?_C?_^~?C?_C?_C?_F?w??OA?OA?OA?wF?_?_C?_C?_CB_[B??_C?_C?_C?wFF??OA?OA?OA?wFF_?C?_C?_C?_[Bbw??_C?_C?_C?wF~}wA?OA?OA?OF?~{??C?_C?_C?_[B~w"), 245 colors: 9, 246 dsatur: setOf(9, 10, 11), 247 randomized: setOf(9, 10, 11, 12, 13, 14, 15, 16, 17), 248 rlf: setOf(9, 10, 11, 12), 249 sanSegundo: setOf(9, 10, 11), 250 welshPowell: setOf(9, 10, 11, 12, 13), 251 252 partial: map[int64]int{1: 3}, 253 }, 254 255 // Test graphs from NetworkX. 256 { 257 name: "cs_shc", 258 g: graph6.Graph("Djs"), 259 colors: 3, 260 randomized: setOf(3, 4), 261 }, 262 { 263 name: "gis_hc", 264 g: graph6.Graph("E?ow"), 265 colors: 2, 266 randomized: setOf(2, 3), 267 }, 268 { 269 name: "gis_shc|rs_shc", // https://hog.grinvin.org/ViewGraphInfo.action?id=594 270 g: graph6.Graph("CR"), 271 colors: 2, 272 randomized: setOf(2, 3), 273 }, 274 { 275 name: "lf_hc", 276 g: graph6.Graph(`F\^E?`), 277 colors: 3, 278 dsatur: setOf(3, 4), 279 randomized: setOf(3, 4), 280 rlf: setOf(3, 4), 281 sanSegundo: setOf(3, 4), 282 welshPowell: setOf(3, 4), 283 }, 284 { 285 name: "lf_shc", 286 g: graph6.Graph("ELQ?"), 287 colors: 2, 288 randomized: setOf(2, 3), 289 welshPowell: setOf(2, 3), 290 }, 291 { 292 name: "lfi_hc", 293 g: graph6.Graph("Hhe[b@_"), 294 colors: 3, 295 dsatur: setOf(3, 4), 296 randomized: setOf(3, 4), 297 sanSegundo: setOf(3, 4), 298 welshPowell: setOf(3, 4), 299 }, 300 { 301 name: "lfi_shc|slf_shc", 302 g: graph6.Graph("FheZ?"), 303 colors: 3, 304 dsatur: setOf(3, 4), 305 randomized: setOf(3, 4), 306 welshPowell: setOf(3, 4), 307 }, 308 { 309 name: "no_solo", // https://hog.grinvin.org/ViewGraphInfo.action?id=264, https://hog.grinvin.org/ViewGraphInfo.action?id=498 310 g: graph6.Graph("K????AccaQHG"), 311 colors: 2, 312 randomized: setOf(2, 3), 313 }, 314 { 315 name: "sl_hc", 316 g: graph6.Graph("Gzg[Yk"), 317 colors: 4, 318 dsatur: setOf(4, 5), 319 randomized: setOf(4, 5), 320 sanSegundo: setOf(4, 5), 321 welshPowell: setOf(4, 5), 322 }, 323 { 324 name: "sl_shc", 325 g: graph6.Graph("E{Sw"), 326 colors: 3, 327 dsatur: setOf(3, 4), 328 randomized: setOf(3, 4), 329 welshPowell: setOf(3, 4), 330 }, 331 { 332 name: "slf_hc", 333 g: graph6.Graph("G}`?W["), 334 colors: 3, 335 dsatur: setOf(3, 4), 336 randomized: setOf(3, 4), 337 rlf: setOf(3, 4), 338 welshPowell: setOf(3, 4), 339 }, 340 { 341 name: "sli_hc", 342 g: graph6.Graph("H{czYtt"), 343 colors: 4, 344 dsatur: setOf(4, 5), 345 randomized: setOf(4, 5), 346 welshPowell: setOf(4, 5), 347 }, 348 { 349 name: "sli_shc", 350 g: graph6.Graph("FxdSW"), 351 colors: 3, 352 dsatur: setOf(3, 4), 353 randomized: setOf(3, 4), 354 sanSegundo: setOf(3, 4), 355 welshPowell: setOf(3, 4), 356 }, 357 { 358 name: "rsi_shc", 359 g: graph6.Graph("EheW"), 360 colors: 3, 361 randomized: setOf(3, 4), 362 }, 363 { 364 name: "V_plus_not_in_A_cal", 365 g: graph6.Graph("HQQ?W__"), 366 colors: 2, 367 randomized: setOf(2, 3), 368 }, 369 370 // DIMACS queens graphs 371 { 372 name: "queen5_5", 373 g: graph6.Graph("X~~FJk~F|KIxizS^dF{iWQjcdV[dFyQb}KiWOdVHAT\\acg~acg~"), 374 colors: 5, 375 dsatur: setOf(5), 376 randomized: setOf(5, 6, 7, 8, 9, 10), 377 rlf: setOf(5), 378 sanSegundo: setOf(5), 379 welshPowell: setOf(5, 6, 7), 380 }, 381 { 382 name: "queen6_6", 383 g: graph6.Graph("c~~}FDrMw~`~goSwtMYhvIF{SN{dEAQfCehrcTMyPO~ca`~acgoPQSwcCtMWcahvaQIF|CcSN{KSdEAAIQfC__ehrCCcTMwSQPO~ogca`~"), 384 colors: 7, 385 dsatur: setOf(7, 8, 9), 386 randomized: setOf(7, 8, 9, 10, 11, 12), 387 rlf: setOf(7, 8), 388 sanSegundo: setOf(7, 8), 389 welshPowell: setOf(7, 8, 9), 390 }, 391 { 392 name: "queen7_7", 393 g: graph6.Graph("p~~~}B`[XrbnB~@~sKAb`iMLS[xS\\wgN{IB~cSKAPPocibbcibfQDPvc`O^wcIB~aQIE@CcS[HCdS[WaQiM]GcIbnPC`O^xCQD@~ogca`_Ogcab`GCQTPpa@CdS[wQGcIbn`GaOgN|APC`O^{EDCcSKA@AaQIMC_OGcibbCA@CdS[wOHCQDPv_gQGcIB~_gQGcIB~"), 394 colors: 7, 395 dsatur: setOf(7, 8, 9, 10, 11), 396 randomized: setOf(7, 8, 9, 10, 11, 12, 13), 397 rlf: setOf(7, 8, 9), 398 sanSegundo: setOf(7, 8, 9), 399 welshPowell: setOf(7, 8, 9, 10, 11, 12), 400 }, 401 { 402 name: "queen8_8", 403 long: true, 404 g: graph6.Graph("~?@?~~~~~?wJ`fFFNBn_~wF~gK@OwLOwYg[[iFNDOzwSB~_gF~cIB?QDB_cdOw[ciFFQQg[{cDOzwcD?~wQA_^}GcIB?PC`OwHCQTB`aKciFFaCciFNPAOTBncOcD?~waC_gF~`GaOgK@APC`OwHAPCdOwW_GqQg[[GaCciFN`COcDOzyCPAOSB~cGaC_gF~_gQGcIB?OSHCQDB_c@APCdOwW_GAKciFFA?aGQQg[{C`COcDOz{C`COcD?~yAOaGQA_^}@_gQGcIB?OCDAPC`OwH?OCHCQTB`a?_GAKciFFA?_GaCciFN@?QCPAOTBn_SC`COcD?~{A_cGaC_gF~"), 405 colors: 9, 406 dsatur: setOf(9, 10, 11, 12), 407 randomized: setOf(9, 10, 11, 12, 13, 14, 15), 408 rlf: setOf(9, 10), 409 sanSegundo: setOf(9, 10, 11), 410 welshPowell: setOf(9, 10, 11, 12, 13, 14, 15), 411 }, 412 } 413 414 func setOf(vals ...int) set.Ints { 415 s := make(set.Ints) 416 for _, v := range vals { 417 s.Add(v) 418 } 419 return s 420 } 421 422 func TestDsatur(t *testing.T) { 423 for _, test := range coloringTests { 424 for _, partial := range []map[int64]int{nil, test.partial} { 425 k, colors, err := Dsatur(test.g, partial) 426 427 if partial == nil && k != test.colors && !test.dsatur.Has(k) { 428 t.Errorf("unexpected chromatic number for %q: got:%d want:%d or in %v\ncolors:%v", 429 test.name, k, test.colors, test.dsatur, colors) 430 } 431 if s := Sets(colors); len(s) != k { 432 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 433 } 434 if missing, ok := isCompleteColoring(colors, test.g); !ok { 435 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 436 } 437 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 438 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 439 test.name, xid, yid, colors) 440 } 441 if err != nil { 442 t.Errorf("unexpected error: %v", err) 443 } 444 445 for id, c := range partial { 446 if colors[id] != c { 447 t.Errorf("coloring not consistent with input partial for %q:\ngot:%v\nwant superset of:%v", 448 test.name, colors, partial) 449 break 450 } 451 } 452 } 453 } 454 } 455 456 func TestDsaturExact(t *testing.T) { 457 timeout := time.Microsecond 458 for _, test := range coloringTests { 459 for _, useTimeout := range []bool{false, true} { 460 if test.long && !*runLong && !useTimeout { 461 continue 462 } 463 var ( 464 term Terminator 465 cancel func() 466 ) 467 if useTimeout { 468 term, cancel = context.WithTimeout(context.Background(), timeout) 469 } else { 470 // Set a backstop to safeguard against occasional long running 471 // cases crashing the entire test set so get as much time as we can. 472 deadline, ok := t.Deadline() 473 if ok { 474 // But make sure we are faster than the watchdog. 475 deadline = deadline.Add(-10 * time.Second) 476 if deadline.Before(time.Now()) { 477 t.Errorf("we ran out of time by %q", test.name) 478 } 479 term, cancel = context.WithDeadline(context.Background(), deadline) 480 } else { 481 cancel = func() {} 482 } 483 } 484 k, colors, err := DsaturExact(term, test.g) 485 cancel() 486 if k != test.colors && (useTimeout && !test.dsatur.Has(k)) { 487 t.Errorf("unexpected chromatic number for %q timeout=%t: got:%d want:%d or in %v\ncolors:%v", 488 test.name, useTimeout, k, test.colors, test.dsatur, colors) 489 } 490 if s := Sets(colors); len(s) != k { 491 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 492 } 493 if missing, ok := isCompleteColoring(colors, test.g); !ok { 494 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 495 } 496 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 497 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 498 test.name, xid, yid, colors) 499 } 500 if err != nil && !useTimeout { 501 if err != context.DeadlineExceeded { 502 t.Errorf("unexpected error for %q: %v", test.name, err) 503 } else { 504 t.Logf("test ran too long for %q", test.name) 505 } 506 } 507 } 508 } 509 } 510 511 func TestRandomized(t *testing.T) { 512 for seed := uint64(1); seed <= 1000; seed++ { 513 for _, test := range coloringTests { 514 for _, partial := range []map[int64]int{nil, test.partial} { 515 k, colors, err := Randomized(test.g, partial, rand.NewSource(seed)) 516 517 if partial == nil && k != test.colors && !test.randomized.Has(k) { 518 t.Errorf("unexpected chromatic number for %q with seed=%d: got:%d want:%d or in %v\ncolors:%v", 519 test.name, seed, k, test.colors, test.randomized, colors) 520 } 521 if s := Sets(colors); len(s) != k { 522 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 523 } 524 if missing, ok := isCompleteColoring(colors, test.g); !ok { 525 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 526 } 527 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 528 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 529 test.name, xid, yid, colors) 530 } 531 if err != nil { 532 t.Errorf("unexpected error: %v", err) 533 } 534 535 for id, c := range partial { 536 if colors[id] != c { 537 t.Errorf("coloring not consistent with input partial for %q:\ngot:%v\nwant superset of:%v", 538 test.name, colors, partial) 539 break 540 } 541 } 542 } 543 } 544 } 545 } 546 547 func TestRecursiveLargestFirst(t *testing.T) { 548 for _, test := range coloringTests { 549 k, colors := RecursiveLargestFirst(test.g) 550 if k != test.colors && !test.rlf.Has(k) { 551 t.Errorf("unexpected chromatic number for %q: got:%d want:%d", 552 test.name, k, test.colors) 553 } 554 if s := Sets(colors); len(s) != k { 555 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 556 } 557 if missing, ok := isCompleteColoring(colors, test.g); !ok { 558 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 559 } 560 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 561 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 562 test.name, xid, yid, colors) 563 } 564 } 565 } 566 567 func TestSanSegundo(t *testing.T) { 568 for _, test := range coloringTests { 569 for _, partial := range []map[int64]int{nil, test.partial} { 570 k, colors, err := SanSegundo(test.g, partial) 571 572 if partial == nil && k != test.colors && !test.sanSegundo.Has(k) { 573 t.Errorf("unexpected chromatic number for %q: got:%d want:%d\ncolors:%v", 574 test.name, k, test.colors, colors) 575 } 576 if s := Sets(colors); len(s) != k { 577 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 578 } 579 if missing, ok := isCompleteColoring(colors, test.g); !ok { 580 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 581 } 582 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 583 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 584 test.name, xid, yid, colors) 585 } 586 if err != nil { 587 t.Errorf("unexpected error: %v", err) 588 } 589 590 for id, c := range partial { 591 if colors[id] != c { 592 t.Errorf("coloring not consistent with input partial for %q:\ngot:%v\nwant superset of:%v", 593 test.name, colors, partial) 594 break 595 } 596 } 597 } 598 } 599 } 600 601 func TestWelshPowell(t *testing.T) { 602 for _, test := range coloringTests { 603 for _, partial := range []map[int64]int{nil, test.partial} { 604 k, colors, err := WelshPowell(test.g, partial) 605 606 if partial == nil && k != test.colors && !test.welshPowell.Has(k) { 607 t.Errorf("unexpected chromatic number for %q: got:%d want:%d", 608 test.name, k, test.colors) 609 } 610 if s := Sets(colors); len(s) != k { 611 t.Errorf("mismatch between number of color sets and k: |sets|=%d k=%d", len(s), k) 612 } 613 if missing, ok := isCompleteColoring(colors, test.g); !ok { 614 t.Errorf("incomplete coloring for %q: missing %d\ngot:%v", test.name, missing, colors) 615 } 616 if xid, yid, ok := isValidColoring(colors, test.g); !ok { 617 t.Errorf("invalid coloring for %q: %d--%d match color\ncolors:%v", 618 test.name, xid, yid, colors) 619 } 620 if err != nil { 621 t.Errorf("unexpected error: %v", err) 622 } 623 624 for id, c := range partial { 625 if colors[id] != c { 626 t.Errorf("coloring not consistent with input partial for %q:\ngot:%v\nwant superset of:%v", 627 test.name, colors, partial) 628 break 629 } 630 } 631 } 632 } 633 } 634 635 var newPartialTests = []struct { 636 partial map[int64]int 637 g graph.Undirected 638 wantValid bool 639 }{ 640 { 641 partial: map[int64]int{0: 1, 1: 1}, 642 g: undirectedGraphFrom([]intset{ 643 0: linksTo(1, 2), 644 1: linksTo(2), 645 2: nil, 646 }), 647 wantValid: false, 648 }, 649 { 650 partial: map[int64]int{0: 0, 1: 1}, 651 g: undirectedGraphFrom([]intset{ 652 0: linksTo(1, 2), 653 1: linksTo(2), 654 2: nil, 655 }), 656 wantValid: true, 657 }, 658 { 659 partial: map[int64]int{0: 0, 1: 1, 3: 3}, 660 g: undirectedGraphFrom([]intset{ 661 0: linksTo(1, 2), 662 1: linksTo(2), 663 2: nil, 664 }), 665 wantValid: false, 666 }, 667 { 668 partial: map[int64]int{0: 0, 1: 1, 2: 2}, 669 g: undirectedGraphFrom([]intset{ 670 0: linksTo(1, 2), 671 1: linksTo(2), 672 2: nil, 673 }), 674 wantValid: true, 675 }, 676 { 677 partial: nil, 678 g: undirectedGraphFrom([]intset{ 679 0: linksTo(1, 2), 680 1: linksTo(2), 681 2: nil, 682 }), 683 wantValid: true, 684 }, 685 } 686 687 func TestNewPartial(t *testing.T) { 688 for i, test := range newPartialTests { 689 gotPartial, gotValid := newPartial(test.partial, test.g) 690 if gotValid != test.wantValid { 691 t.Errorf("unexpected validity for test %d: got:%t want:%t", 692 i, gotValid, test.wantValid) 693 } 694 xid, yid, ok := isValidColoring(gotPartial, test.g) 695 if !ok { 696 t.Errorf("invalid partial returned for test %d: %d--%d match color\ncolors:%v", 697 i, xid, yid, gotPartial) 698 } 699 700 } 701 } 702 703 func isCompleteColoring(colors map[int64]int, g graph.Undirected) (missing int64, ok bool) { 704 for _, n := range graph.NodesOf(g.Nodes()) { 705 if _, ok := colors[n.ID()]; !ok { 706 return n.ID(), false 707 } 708 } 709 return 0, true 710 } 711 712 func isValidColoring(colors map[int64]int, g graph.Undirected) (x, y int64, ok bool) { 713 for xid, c := range colors { 714 to := g.From(xid) 715 for to.Next() { 716 yid := to.Node().ID() 717 if oc, ok := colors[yid]; ok && c == oc { 718 return xid, yid, false 719 } 720 } 721 } 722 return 0, 0, true 723 } 724 725 // intset is an integer set. 726 type intset map[int64]struct{} 727 728 func linksTo(i ...int64) intset { 729 if len(i) == 0 { 730 return nil 731 } 732 s := make(intset) 733 for _, v := range i { 734 s[v] = struct{}{} 735 } 736 return s 737 } 738 739 func undirectedGraphFrom(g []intset) graph.Undirected { 740 dg := simple.NewUndirectedGraph() 741 for u, e := range g { 742 for v := range e { 743 dg.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 744 } 745 } 746 return dg 747 } 748 749 func BenchmarkColoring(b *testing.B) { 750 for _, bench := range coloringTests { 751 b.Run(bench.name, func(b *testing.B) { 752 b.Run("Dsatur", func(b *testing.B) { 753 for i := 0; i < b.N; i++ { 754 _, _, err := Dsatur(bench.g, nil) 755 if err != nil { 756 b.Fatalf("coloring failed: %v", err) 757 } 758 } 759 }) 760 if !bench.long || *runLong { 761 b.Run("DsaturExact", func(b *testing.B) { 762 for i := 0; i < b.N; i++ { 763 _, _, err := DsaturExact(nil, bench.g) 764 if err != nil { 765 b.Fatalf("coloring failed: %v", err) 766 } 767 } 768 }) 769 } 770 b.Run("RecursiveLargestFirst", func(b *testing.B) { 771 for i := 0; i < b.N; i++ { 772 RecursiveLargestFirst(bench.g) 773 } 774 }) 775 b.Run("SanSegundo", func(b *testing.B) { 776 for i := 0; i < b.N; i++ { 777 _, _, err := SanSegundo(bench.g, nil) 778 if err != nil { 779 b.Fatalf("coloring failed: %v", err) 780 } 781 } 782 }) 783 b.Run("WelshPowell", func(b *testing.B) { 784 for i := 0; i < b.N; i++ { 785 _, _, err := WelshPowell(bench.g, nil) 786 if err != nil { 787 b.Fatalf("coloring failed: %v", err) 788 } 789 } 790 }) 791 }) 792 } 793 }