github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/topo/johnson_cycles.go (about)

     1  // Copyright ©2015 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  	"sort"
     9  
    10  	"github.com/jingcheng-WU/gonum/graph"
    11  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    12  	"github.com/jingcheng-WU/gonum/graph/internal/set"
    13  	"github.com/jingcheng-WU/gonum/graph/iterator"
    14  )
    15  
    16  // johnson implements Johnson's "Finding all the elementary
    17  // circuits of a directed graph" algorithm. SIAM J. Comput. 4(1):1975.
    18  //
    19  // Comments in the johnson methods are kept in sync with the comments
    20  // and labels from the paper.
    21  type johnson struct {
    22  	adjacent johnsonGraph // SCC adjacency list.
    23  	b        []set.Ints   // Johnson's "B-list".
    24  	blocked  []bool
    25  	s        int
    26  
    27  	stack []graph.Node
    28  
    29  	result [][]graph.Node
    30  }
    31  
    32  // DirectedCyclesIn returns the set of elementary cycles in the graph g.
    33  func DirectedCyclesIn(g graph.Directed) [][]graph.Node {
    34  	jg := johnsonGraphFrom(g)
    35  	j := johnson{
    36  		adjacent: jg,
    37  		b:        make([]set.Ints, len(jg.orig)),
    38  		blocked:  make([]bool, len(jg.orig)),
    39  	}
    40  
    41  	// len(j.nodes) is the order of g.
    42  	for j.s < len(j.adjacent.orig)-1 {
    43  		// We use the previous SCC adjacency to reduce the work needed.
    44  		sccs := TarjanSCC(j.adjacent.subgraph(j.s))
    45  		// A_k = adjacency structure of strong component K with least
    46  		//       vertex in subgraph of G induced by {s, s+1, ... ,n}.
    47  		j.adjacent = j.adjacent.sccSubGraph(sccs, 2) // Only allow SCCs with >= 2 vertices.
    48  		if j.adjacent.order() == 0 {
    49  			break
    50  		}
    51  
    52  		// s = least vertex in V_k
    53  		if s := j.adjacent.leastVertexIndex(); s < j.s {
    54  			j.s = s
    55  		}
    56  		for i, v := range j.adjacent.orig {
    57  			if !j.adjacent.nodes.Has(v.ID()) {
    58  				continue
    59  			}
    60  			if len(j.adjacent.succ[v.ID()]) > 0 {
    61  				j.blocked[i] = false
    62  				j.b[i] = make(set.Ints)
    63  			}
    64  		}
    65  		//L3:
    66  		_ = j.circuit(j.s)
    67  		j.s++
    68  	}
    69  
    70  	return j.result
    71  }
    72  
    73  // circuit is the CIRCUIT sub-procedure in the paper.
    74  func (j *johnson) circuit(v int) bool {
    75  	f := false
    76  	n := j.adjacent.orig[v]
    77  	j.stack = append(j.stack, n)
    78  	j.blocked[v] = true
    79  
    80  	//L1:
    81  	for w := range j.adjacent.succ[n.ID()] {
    82  		w := j.adjacent.indexOf(w)
    83  		if w == j.s {
    84  			// Output circuit composed of stack followed by s.
    85  			r := make([]graph.Node, len(j.stack)+1)
    86  			copy(r, j.stack)
    87  			r[len(r)-1] = j.adjacent.orig[j.s]
    88  			j.result = append(j.result, r)
    89  			f = true
    90  		} else if !j.blocked[w] {
    91  			if j.circuit(w) {
    92  				f = true
    93  			}
    94  		}
    95  	}
    96  
    97  	//L2:
    98  	if f {
    99  		j.unblock(v)
   100  	} else {
   101  		for w := range j.adjacent.succ[n.ID()] {
   102  			j.b[j.adjacent.indexOf(w)].Add(v)
   103  		}
   104  	}
   105  	j.stack = j.stack[:len(j.stack)-1]
   106  
   107  	return f
   108  }
   109  
   110  // unblock is the UNBLOCK sub-procedure in the paper.
   111  func (j *johnson) unblock(u int) {
   112  	j.blocked[u] = false
   113  	for w := range j.b[u] {
   114  		j.b[u].Remove(w)
   115  		if j.blocked[w] {
   116  			j.unblock(w)
   117  		}
   118  	}
   119  }
   120  
   121  // johnsonGraph is an edge list representation of a graph with helpers
   122  // necessary for Johnson's algorithm
   123  type johnsonGraph struct {
   124  	// Keep the original graph nodes and a
   125  	// look-up to into the non-sparse
   126  	// collection of potentially sparse IDs.
   127  	orig  []graph.Node
   128  	index map[int64]int
   129  
   130  	nodes set.Int64s
   131  	succ  map[int64]set.Int64s
   132  }
   133  
   134  // johnsonGraphFrom returns a deep copy of the graph g.
   135  func johnsonGraphFrom(g graph.Directed) johnsonGraph {
   136  	nodes := graph.NodesOf(g.Nodes())
   137  	sort.Sort(ordered.ByID(nodes))
   138  	c := johnsonGraph{
   139  		orig:  nodes,
   140  		index: make(map[int64]int, len(nodes)),
   141  
   142  		nodes: make(set.Int64s, len(nodes)),
   143  		succ:  make(map[int64]set.Int64s),
   144  	}
   145  	for i, u := range nodes {
   146  		uid := u.ID()
   147  		c.index[uid] = i
   148  		to := g.From(uid)
   149  		for to.Next() {
   150  			v := to.Node()
   151  			if c.succ[uid] == nil {
   152  				c.succ[uid] = make(set.Int64s)
   153  				c.nodes.Add(uid)
   154  			}
   155  			c.nodes.Add(v.ID())
   156  			c.succ[uid].Add(v.ID())
   157  		}
   158  	}
   159  	return c
   160  }
   161  
   162  // order returns the order of the graph.
   163  func (g johnsonGraph) order() int { return g.nodes.Count() }
   164  
   165  // indexOf returns the index of the retained node for the given node ID.
   166  func (g johnsonGraph) indexOf(id int64) int {
   167  	return g.index[id]
   168  }
   169  
   170  // leastVertexIndex returns the index into orig of the least vertex.
   171  func (g johnsonGraph) leastVertexIndex() int {
   172  	for _, v := range g.orig {
   173  		if g.nodes.Has(v.ID()) {
   174  			return g.indexOf(v.ID())
   175  		}
   176  	}
   177  	panic("johnsonCycles: empty set")
   178  }
   179  
   180  // subgraph returns a subgraph of g induced by {s, s+1, ... , n}. The
   181  // subgraph is destructively generated in g.
   182  func (g johnsonGraph) subgraph(s int) johnsonGraph {
   183  	sn := g.orig[s].ID()
   184  	for u, e := range g.succ {
   185  		if u < sn {
   186  			g.nodes.Remove(u)
   187  			delete(g.succ, u)
   188  			continue
   189  		}
   190  		for v := range e {
   191  			if v < sn {
   192  				g.succ[u].Remove(v)
   193  			}
   194  		}
   195  	}
   196  	return g
   197  }
   198  
   199  // sccSubGraph returns the graph of the tarjan's strongly connected
   200  // components with each SCC containing at least min vertices.
   201  // sccSubGraph returns nil if there is no SCC with at least min
   202  // members.
   203  func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
   204  	if len(g.nodes) == 0 {
   205  		g.nodes = nil
   206  		g.succ = nil
   207  		return g
   208  	}
   209  	sub := johnsonGraph{
   210  		orig:  g.orig,
   211  		index: g.index,
   212  		nodes: make(set.Int64s),
   213  		succ:  make(map[int64]set.Int64s),
   214  	}
   215  
   216  	var n int
   217  	for _, scc := range sccs {
   218  		if len(scc) < min {
   219  			continue
   220  		}
   221  		n++
   222  		for _, u := range scc {
   223  			for _, v := range scc {
   224  				if _, ok := g.succ[u.ID()][v.ID()]; ok {
   225  					if sub.succ[u.ID()] == nil {
   226  						sub.succ[u.ID()] = make(set.Int64s)
   227  						sub.nodes.Add(u.ID())
   228  					}
   229  					sub.nodes.Add(v.ID())
   230  					sub.succ[u.ID()].Add(v.ID())
   231  				}
   232  			}
   233  		}
   234  	}
   235  	if n == 0 {
   236  		g.nodes = nil
   237  		g.succ = nil
   238  		return g
   239  	}
   240  
   241  	return sub
   242  }
   243  
   244  // Nodes is required to satisfy Tarjan.
   245  func (g johnsonGraph) Nodes() graph.Nodes {
   246  	n := make([]graph.Node, 0, len(g.nodes))
   247  	for id := range g.nodes {
   248  		n = append(n, johnsonGraphNode(id))
   249  	}
   250  	return iterator.NewOrderedNodes(n)
   251  }
   252  
   253  // Successors is required to satisfy Tarjan.
   254  func (g johnsonGraph) From(id int64) graph.Nodes {
   255  	adj := g.succ[id]
   256  	if len(adj) == 0 {
   257  		return graph.Empty
   258  	}
   259  	succ := make([]graph.Node, 0, len(adj))
   260  	for id := range adj {
   261  		succ = append(succ, johnsonGraphNode(id))
   262  	}
   263  	return iterator.NewOrderedNodes(succ)
   264  }
   265  
   266  func (johnsonGraph) Has(int64) bool {
   267  	panic("topo: unintended use of johnsonGraph")
   268  }
   269  func (johnsonGraph) Node(int64) graph.Node {
   270  	panic("topo: unintended use of johnsonGraph")
   271  }
   272  func (johnsonGraph) HasEdgeBetween(_, _ int64) bool {
   273  	panic("topo: unintended use of johnsonGraph")
   274  }
   275  func (johnsonGraph) Edge(_, _ int64) graph.Edge {
   276  	panic("topo: unintended use of johnsonGraph")
   277  }
   278  func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool {
   279  	panic("topo: unintended use of johnsonGraph")
   280  }
   281  func (johnsonGraph) To(int64) graph.Nodes {
   282  	panic("topo: unintended use of johnsonGraph")
   283  }
   284  
   285  type johnsonGraphNode int64
   286  
   287  func (n johnsonGraphNode) ID() int64 { return int64(n) }