go-hep.org/x/hep@v0.38.1/fwk/utils/tarjan/tarjan.go (about)

     1  // Copyright ©2017 The go-hep 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  // Copyright (c) 2013 - Max Persson <max@looplab.se>
     6  // Copyright (c) 2010-2013 - Gustavo Niemeyer <gustavo@niemeyer.net>
     7  //
     8  // Licensed under the Apache License, Version 2.0 (the "License");
     9  // you may not use this file except in compliance with the License.
    10  // You may obtain a copy of the License at
    11  //
    12  //     http://www.apache.org/licenses/LICENSE-2.0
    13  //
    14  // Unless required by applicable law or agreed to in writing, software
    15  // distributed under the License is distributed on an "AS IS" BASIS,
    16  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17  // See the License for the specific language governing permissions and
    18  // limitations under the License.
    19  
    20  // package tarjan implements a graph loop detection algorithm called Tarjan's algorithm.
    21  //
    22  // The algorithm takes a input graph and produces a slice where each item is a
    23  // slice of strongly connected vertices. The input graph is in form of a map
    24  // where the key is a graph vertex and the value is the edges in for of a slice
    25  // of vertices.
    26  //
    27  // Algorithm description:
    28  // http://en.wikipedia.org/wiki/Tarjan’s_strongly_connected_components_algorithm
    29  //
    30  // Based on an implementation by Gustavo Niemeyer (in mgo/txn):
    31  // http://bazaar.launchpad.net/+branch/mgo/v2/view/head:/txn/tarjan.go
    32  package tarjan // import "go-hep.org/x/hep/fwk/utils/tarjan"
    33  
    34  // Connections creates a slice where each item is a slice of strongly connected vertices.
    35  //
    36  // If a slice item contains only one vertex there are no loops. A loop on the
    37  // vertex itself is also a connected group.
    38  //
    39  // The example shows the same graph as in the Wikipedia article.
    40  func Connections(graph map[any][]any) [][]any {
    41  	g := &data{
    42  		graph: graph,
    43  		nodes: make([]node, 0, len(graph)),
    44  		index: make(map[any]int, len(graph)),
    45  	}
    46  	for v := range g.graph {
    47  		if _, ok := g.index[v]; !ok {
    48  			g.strongConnect(v)
    49  		}
    50  	}
    51  	return g.output
    52  }
    53  
    54  // data contains all common data for a single operation.
    55  type data struct {
    56  	graph  map[any][]any
    57  	nodes  []node
    58  	stack  []any
    59  	index  map[any]int
    60  	output [][]any
    61  }
    62  
    63  // node stores data for a single vertex in the connection process.
    64  type node struct {
    65  	lowlink int
    66  	stacked bool
    67  }
    68  
    69  // strongConnect runs Tarjan's algorithm recursivley and outputs a grouping of
    70  // strongly connected vertices.
    71  func (data *data) strongConnect(v any) *node {
    72  	index := len(data.nodes)
    73  	data.index[v] = index
    74  	data.stack = append(data.stack, v)
    75  	data.nodes = append(data.nodes, node{lowlink: index, stacked: true})
    76  	node := &data.nodes[index]
    77  
    78  	for _, w := range data.graph[v] {
    79  		i, seen := data.index[w]
    80  		if !seen {
    81  			n := data.strongConnect(w)
    82  			if n.lowlink < node.lowlink {
    83  				node.lowlink = n.lowlink
    84  			}
    85  		} else if data.nodes[i].stacked {
    86  			if i < node.lowlink {
    87  				node.lowlink = i
    88  			}
    89  		}
    90  	}
    91  
    92  	if node.lowlink == index {
    93  		var vertices []any
    94  		i := len(data.stack) - 1
    95  		for {
    96  			w := data.stack[i]
    97  			stackIndex := data.index[w]
    98  			data.nodes[stackIndex].stacked = false
    99  			vertices = append(vertices, w)
   100  			if stackIndex == index {
   101  				break
   102  			}
   103  			i--
   104  		}
   105  		data.stack = data.stack[:i]
   106  		data.output = append(data.output, vertices)
   107  	}
   108  
   109  	return node
   110  }