github.com/gopherd/gonum@v0.0.4/graph/topo/topo.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  	"github.com/gopherd/gonum/graph"
     9  	"github.com/gopherd/gonum/graph/internal/ordered"
    10  	"github.com/gopherd/gonum/graph/traverse"
    11  )
    12  
    13  // IsPathIn returns whether path is a path in g.
    14  //
    15  // As special cases, IsPathIn returns true for a zero length path or for
    16  // a path of length 1 when the node in path exists in the graph.
    17  func IsPathIn(g graph.Graph, path []graph.Node) bool {
    18  	switch len(path) {
    19  	case 0:
    20  		return true
    21  	case 1:
    22  		return g.Node(path[0].ID()) != nil
    23  	default:
    24  		var canReach func(uid, vid int64) bool
    25  		switch g := g.(type) {
    26  		case graph.Directed:
    27  			canReach = g.HasEdgeFromTo
    28  		default:
    29  			canReach = g.HasEdgeBetween
    30  		}
    31  
    32  		for i, u := range path[:len(path)-1] {
    33  			if !canReach(u.ID(), path[i+1].ID()) {
    34  				return false
    35  			}
    36  		}
    37  		return true
    38  	}
    39  }
    40  
    41  // PathExistsIn returns whether there is a path in g starting at 'from' extending
    42  // to 'to'.
    43  //
    44  // PathExistsIn exists as a helper function. If many tests for path existence
    45  // are being performed, other approaches will be more efficient.
    46  func PathExistsIn(g graph.Graph, from, to graph.Node) bool {
    47  	var t traverse.BreadthFirst
    48  	return t.Walk(g, from, func(n graph.Node, _ int) bool { return n.ID() == to.ID() }) != nil
    49  }
    50  
    51  // ConnectedComponents returns the connected components of the undirected graph g.
    52  func ConnectedComponents(g graph.Undirected) [][]graph.Node {
    53  	var (
    54  		w  traverse.DepthFirst
    55  		c  []graph.Node
    56  		cc [][]graph.Node
    57  	)
    58  	during := func(n graph.Node) {
    59  		c = append(c, n)
    60  	}
    61  	after := func() {
    62  		cc = append(cc, []graph.Node(nil))
    63  		cc[len(cc)-1] = append(cc[len(cc)-1], c...)
    64  		c = c[:0]
    65  	}
    66  	w.WalkAll(g, nil, after, during)
    67  
    68  	return cc
    69  }
    70  
    71  // Equal returns whether two graphs are topologically equal. To be
    72  // considered topologically equal, a and b must have identical sets
    73  // of nodes and be identically traversable.
    74  func Equal(a, b graph.Graph) bool {
    75  	aNodes := a.Nodes()
    76  	bNodes := b.Nodes()
    77  	if aNodes.Len() != bNodes.Len() {
    78  		return false
    79  	}
    80  
    81  	aNodeSlice := graph.NodesOf(aNodes)
    82  	bNodeSlice := graph.NodesOf(bNodes)
    83  	ordered.ByID(aNodeSlice)
    84  	ordered.ByID(bNodeSlice)
    85  	for i, aU := range aNodeSlice {
    86  		id := aU.ID()
    87  		if id != bNodeSlice[i].ID() {
    88  			return false
    89  		}
    90  
    91  		toA := a.From(id)
    92  		toB := b.From(id)
    93  		if toA.Len() != toB.Len() {
    94  			return false
    95  		}
    96  
    97  		aAdjacent := graph.NodesOf(toA)
    98  		bAdjacent := graph.NodesOf(toB)
    99  		ordered.ByID(aAdjacent)
   100  		ordered.ByID(bAdjacent)
   101  		for i, aV := range aAdjacent {
   102  			id := aV.ID()
   103  			if id != bAdjacent[i].ID() {
   104  				return false
   105  			}
   106  		}
   107  	}
   108  
   109  	return true
   110  }