gonum.org/v1/gonum@v0.14.0/graph/topo/tarjan_test.go (about)

     1  // Copyright ©2014 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  	"reflect"
     9  	"testing"
    10  
    11  	"gonum.org/v1/gonum/graph"
    12  	"gonum.org/v1/gonum/graph/internal/ordered"
    13  	"gonum.org/v1/gonum/graph/simple"
    14  )
    15  
    16  type interval struct{ start, end int }
    17  
    18  var tarjanTests = []struct {
    19  	g []intset
    20  
    21  	ambiguousOrder []interval
    22  	want           [][]int64
    23  
    24  	sortedLength      int
    25  	unorderableLength int
    26  	sortable          bool
    27  }{
    28  	{
    29  		g: []intset{
    30  			0: linksTo(1),
    31  			1: linksTo(2, 7),
    32  			2: linksTo(3, 6),
    33  			3: linksTo(4),
    34  			4: linksTo(2, 5),
    35  			6: linksTo(3, 5),
    36  			7: linksTo(0, 6),
    37  		},
    38  
    39  		want: [][]int64{
    40  			{5},
    41  			{2, 3, 4, 6},
    42  			{0, 1, 7},
    43  		},
    44  
    45  		sortedLength:      1,
    46  		unorderableLength: 2,
    47  		sortable:          false,
    48  	},
    49  	{
    50  		g: []intset{
    51  			0: linksTo(1, 2, 3),
    52  			1: linksTo(2),
    53  			2: linksTo(3),
    54  			3: linksTo(1),
    55  		},
    56  
    57  		want: [][]int64{
    58  			{1, 2, 3},
    59  			{0},
    60  		},
    61  
    62  		sortedLength:      1,
    63  		unorderableLength: 1,
    64  		sortable:          false,
    65  	},
    66  	{
    67  		g: []intset{
    68  			0: linksTo(1),
    69  			1: linksTo(0, 2),
    70  			2: linksTo(1),
    71  		},
    72  
    73  		want: [][]int64{
    74  			{0, 1, 2},
    75  		},
    76  
    77  		sortedLength:      0,
    78  		unorderableLength: 1,
    79  		sortable:          false,
    80  	},
    81  	{
    82  		g: []intset{
    83  			0: linksTo(1),
    84  			1: linksTo(2, 3),
    85  			2: linksTo(4, 5),
    86  			3: linksTo(4, 5),
    87  			4: linksTo(6),
    88  			5: nil,
    89  			6: nil,
    90  		},
    91  
    92  		// Node pairs (2, 3) and (4, 5) are not
    93  		// relatively orderable within each pair.
    94  		ambiguousOrder: []interval{
    95  			{0, 3}, // This includes node 6 since it only needs to be before 4 in topo sort.
    96  			{3, 5},
    97  		},
    98  		want: [][]int64{
    99  			{6}, {5}, {4}, {3}, {2}, {1}, {0},
   100  		},
   101  
   102  		sortedLength: 7,
   103  		sortable:     true,
   104  	},
   105  	{
   106  		g: []intset{
   107  			0: linksTo(1),
   108  			1: linksTo(2, 3, 4),
   109  			2: linksTo(0, 3),
   110  			3: linksTo(4),
   111  			4: linksTo(3),
   112  		},
   113  
   114  		// SCCs are not relatively ordable.
   115  		ambiguousOrder: []interval{
   116  			{0, 2},
   117  		},
   118  		want: [][]int64{
   119  			{0, 1, 2},
   120  			{3, 4},
   121  		},
   122  
   123  		sortedLength:      0,
   124  		unorderableLength: 2,
   125  		sortable:          false,
   126  	},
   127  }
   128  
   129  func TestSort(t *testing.T) {
   130  	for i, test := range tarjanTests {
   131  		g := simple.NewDirectedGraph()
   132  		for u, e := range test.g {
   133  			// Add nodes that are not defined by an edge.
   134  			if g.Node(int64(u)) == nil {
   135  				g.AddNode(simple.Node(u))
   136  			}
   137  			for v := range e {
   138  				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
   139  			}
   140  		}
   141  		sorted, err := Sort(g)
   142  		var gotSortedLen int
   143  		for _, n := range sorted {
   144  			if n != nil {
   145  				gotSortedLen++
   146  			}
   147  		}
   148  		if gotSortedLen != test.sortedLength {
   149  			t.Errorf("unexpected number of sortable nodes for test %d: got:%d want:%d", i, gotSortedLen, test.sortedLength)
   150  		}
   151  		if err == nil != test.sortable {
   152  			t.Errorf("unexpected sortability for test %d: got error: %v want: nil-error=%t", i, err, test.sortable)
   153  		}
   154  		if err != nil && len(err.(Unorderable)) != test.unorderableLength {
   155  			t.Errorf("unexpected number of unorderable nodes for test %d: got:%d want:%d", i, len(err.(Unorderable)), test.unorderableLength)
   156  		}
   157  	}
   158  }
   159  
   160  func TestTarjanSCC(t *testing.T) {
   161  	for i, test := range tarjanTests {
   162  		g := simple.NewDirectedGraph()
   163  		for u, e := range test.g {
   164  			// Add nodes that are not defined by an edge.
   165  			if g.Node(int64(u)) == nil {
   166  				g.AddNode(simple.Node(u))
   167  			}
   168  			for v := range e {
   169  				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
   170  			}
   171  		}
   172  		gotSCCs := TarjanSCC(g)
   173  		// tarjan.strongconnect does range iteration over maps,
   174  		// so sort SCC members to ensure consistent ordering.
   175  		gotIDs := make([][]int64, len(gotSCCs))
   176  		for i, scc := range gotSCCs {
   177  			gotIDs[i] = make([]int64, len(scc))
   178  			for j, id := range scc {
   179  				gotIDs[i][j] = id.ID()
   180  			}
   181  			ordered.Int64s(gotIDs[i])
   182  		}
   183  		for _, iv := range test.ambiguousOrder {
   184  			ordered.BySliceValues(test.want[iv.start:iv.end])
   185  			ordered.BySliceValues(gotIDs[iv.start:iv.end])
   186  		}
   187  		if !reflect.DeepEqual(gotIDs, test.want) {
   188  			t.Errorf("unexpected Tarjan scc result for %d:\n\tgot:%v\n\twant:%v", i, gotIDs, test.want)
   189  		}
   190  	}
   191  }
   192  
   193  var stabilizedSortTests = []struct {
   194  	g []intset
   195  
   196  	want []graph.Node
   197  	err  error
   198  }{
   199  	{
   200  		g: []intset{
   201  			0: linksTo(1),
   202  			1: linksTo(2, 7),
   203  			2: linksTo(3, 6),
   204  			3: linksTo(4),
   205  			4: linksTo(2, 5),
   206  			6: linksTo(3, 5),
   207  			7: linksTo(0, 6),
   208  		},
   209  
   210  		want: []graph.Node{nil, nil, simple.Node(5)},
   211  		err: Unorderable{
   212  			{simple.Node(0), simple.Node(1), simple.Node(7)},
   213  			{simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(6)},
   214  		},
   215  	},
   216  	{
   217  		g: []intset{
   218  			0: linksTo(1, 2, 3),
   219  			1: linksTo(2),
   220  			2: linksTo(3),
   221  			3: linksTo(1),
   222  		},
   223  
   224  		want: []graph.Node{simple.Node(0), nil},
   225  		err: Unorderable{
   226  			{simple.Node(1), simple.Node(2), simple.Node(3)},
   227  		},
   228  	},
   229  	{
   230  		g: []intset{
   231  			0: linksTo(1),
   232  			1: linksTo(0, 2),
   233  			2: linksTo(1),
   234  		},
   235  
   236  		want: []graph.Node{nil},
   237  		err: Unorderable{
   238  			{simple.Node(0), simple.Node(1), simple.Node(2)},
   239  		},
   240  	},
   241  	{
   242  		g: []intset{
   243  			0: linksTo(1),
   244  			1: linksTo(2, 3),
   245  			2: linksTo(4, 5),
   246  			3: linksTo(4, 5),
   247  			4: linksTo(6),
   248  			5: nil,
   249  			6: nil,
   250  		},
   251  
   252  		want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6)},
   253  		err:  nil,
   254  	},
   255  	{
   256  		g: []intset{
   257  			0: linksTo(1),
   258  			1: linksTo(2, 3, 4),
   259  			2: linksTo(0, 3),
   260  			3: linksTo(4),
   261  			4: linksTo(3),
   262  		},
   263  
   264  		want: []graph.Node{nil, nil},
   265  		err: Unorderable{
   266  			{simple.Node(0), simple.Node(1), simple.Node(2)},
   267  			{simple.Node(3), simple.Node(4)},
   268  		},
   269  	},
   270  	{
   271  		g: []intset{
   272  			0: linksTo(1, 2, 3, 4, 5, 6),
   273  			1: linksTo(7),
   274  			2: linksTo(7),
   275  			3: linksTo(7),
   276  			4: linksTo(7),
   277  			5: linksTo(7),
   278  			6: linksTo(7),
   279  			7: nil,
   280  		},
   281  
   282  		want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6), simple.Node(7)},
   283  		err:  nil,
   284  	},
   285  }
   286  
   287  func TestSortStabilized(t *testing.T) {
   288  	for i, test := range stabilizedSortTests {
   289  		g := simple.NewDirectedGraph()
   290  		for u, e := range test.g {
   291  			// Add nodes that are not defined by an edge.
   292  			if g.Node(int64(u)) == nil {
   293  				g.AddNode(simple.Node(u))
   294  			}
   295  			for v := range e {
   296  				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
   297  			}
   298  		}
   299  		got, err := SortStabilized(g, nil)
   300  		if !reflect.DeepEqual(got, test.want) {
   301  			t.Errorf("unexpected sort result for test %d: got:%d want:%d", i, got, test.want)
   302  		}
   303  		if !reflect.DeepEqual(err, test.err) {
   304  			t.Errorf("unexpected sort error for test %d: got:%v want:%v", i, err, test.want)
   305  		}
   306  	}
   307  }