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  }