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

     1  // Copyright ©2017 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  	"gonum.org/v1/gonum/graph"
     9  	"gonum.org/v1/gonum/graph/internal/linear"
    10  	"gonum.org/v1/gonum/graph/internal/set"
    11  )
    12  
    13  // UndirectedCyclesIn returns a set of cycles that forms a cycle basis in the graph g.
    14  // Any cycle in g can be constructed as a symmetric difference of its elements.
    15  func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
    16  	// From "An algorithm for finding a fundamental set of cycles of a graph"
    17  	// https://doi.org/10.1145/363219.363232
    18  
    19  	var cycles [][]graph.Node
    20  	done := make(set.Int64s)
    21  	var tree linear.NodeStack
    22  	nodes := g.Nodes()
    23  	for nodes.Next() {
    24  		n := nodes.Node()
    25  		id := n.ID()
    26  		if done.Has(id) {
    27  			continue
    28  		}
    29  		done.Add(id)
    30  
    31  		tree = tree[:0]
    32  		tree.Push(n)
    33  		from := sets{id: set.Int64s{}}
    34  		to := map[int64]graph.Node{id: n}
    35  
    36  		for tree.Len() != 0 {
    37  			u := tree.Pop()
    38  			uid := u.ID()
    39  			adj := from[uid]
    40  			it := g.From(uid)
    41  			for it.Next() {
    42  				v := it.Node()
    43  				vid := v.ID()
    44  				switch {
    45  				case uid == vid:
    46  					cycles = append(cycles, []graph.Node{u})
    47  				case !from.has(vid):
    48  					done.Add(vid)
    49  					to[vid] = u
    50  					tree.Push(v)
    51  					from.add(uid, vid)
    52  				case !adj.Has(vid):
    53  					c := []graph.Node{v, u}
    54  					adj := from[vid]
    55  					p := to[uid]
    56  					for !adj.Has(p.ID()) {
    57  						c = append(c, p)
    58  						p = to[p.ID()]
    59  					}
    60  					c = append(c, p, c[0])
    61  					cycles = append(cycles, c)
    62  					adj.Add(uid)
    63  				}
    64  			}
    65  		}
    66  	}
    67  
    68  	return cycles
    69  }
    70  
    71  type sets map[int64]set.Int64s
    72  
    73  func (s sets) add(uid, vid int64) {
    74  	e, ok := s[vid]
    75  	if !ok {
    76  		e = make(set.Int64s)
    77  		s[vid] = e
    78  	}
    79  	e.Add(uid)
    80  }
    81  
    82  func (s sets) has(uid int64) bool {
    83  	_, ok := s[uid]
    84  	return ok
    85  }