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 }