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