Files
gonum/graph/topo/paton_cycles.go
Dan Kortschak 2bf857dc70 graph: use iterators directly rather than copying into []graph.Node
There are still uses in test code; these can remain since they simplify
that code.
2020-07-02 07:47:46 +09:30

86 lines
1.8 KiB
Go

// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package topo
import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/linear"
"gonum.org/v1/gonum/graph/internal/set"
)
// UndirectedCyclesIn returns a set of cycles that forms a cycle basis in the graph g.
// Any cycle in g can be constructed as a symmetric difference of its elements.
func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
// From "An algorithm for finding a fundamental set of cycles of a graph"
// https://doi.org/10.1145/363219.363232
var cycles [][]graph.Node
done := make(set.Int64s)
var tree linear.NodeStack
nodes := g.Nodes()
for nodes.Next() {
n := nodes.Node()
id := n.ID()
if done.Has(id) {
continue
}
done.Add(id)
tree = tree[:0]
tree.Push(n)
from := sets{id: set.Int64s{}}
to := map[int64]graph.Node{id: n}
for tree.Len() != 0 {
u := tree.Pop()
uid := u.ID()
adj := from[uid]
it := g.From(uid)
for it.Next() {
v := it.Node()
vid := v.ID()
switch {
case uid == vid:
cycles = append(cycles, []graph.Node{u})
case !from.has(vid):
done.Add(vid)
to[vid] = u
tree.Push(v)
from.add(uid, vid)
case !adj.Has(vid):
c := []graph.Node{v, u}
adj := from[vid]
p := to[uid]
for !adj.Has(p.ID()) {
c = append(c, p)
p = to[p.ID()]
}
c = append(c, p, c[0])
cycles = append(cycles, c)
adj.Add(uid)
}
}
}
}
return cycles
}
type sets map[int64]set.Int64s
func (s sets) add(uid, vid int64) {
e, ok := s[vid]
if !ok {
e = make(set.Int64s)
s[vid] = e
}
e.Add(uid)
}
func (s sets) has(uid int64) bool {
_, ok := s[uid]
return ok
}