gonum.org/v1/gonum@v0.14.0/graph/topo/bron_kerbosch.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  )
    12  
    13  // DegeneracyOrdering returns the degeneracy ordering and the k-cores of
    14  // the undirected graph g.
    15  func DegeneracyOrdering(g graph.Undirected) (order []graph.Node, cores [][]graph.Node) {
    16  	order, offsets := degeneracyOrdering(g)
    17  
    18  	ordered.Reverse(order)
    19  	cores = make([][]graph.Node, len(offsets))
    20  	offset := len(order)
    21  	for i, n := range offsets {
    22  		cores[i] = order[offset-n : offset]
    23  		offset -= n
    24  	}
    25  	return order, cores
    26  }
    27  
    28  // KCore returns the k-core of the undirected graph g with nodes in an
    29  // optimal ordering for the coloring number.
    30  func KCore(k int, g graph.Undirected) []graph.Node {
    31  	order, offsets := degeneracyOrdering(g)
    32  
    33  	var offset int
    34  	for _, n := range offsets[:k] {
    35  		offset += n
    36  	}
    37  	core := make([]graph.Node, len(order)-offset)
    38  	copy(core, order[offset:])
    39  	return core
    40  }
    41  
    42  // degeneracyOrdering is the common code for DegeneracyOrdering and KCore. It
    43  // returns l, the nodes of g in optimal ordering for coloring number and
    44  // s, a set of relative offsets into l for each k-core, where k is an index
    45  // into s.
    46  func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
    47  	nodes := graph.NodesOf(g.Nodes())
    48  
    49  	// The algorithm used here is essentially as described at
    50  	// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
    51  
    52  	// Initialize an output list L in return parameters.
    53  
    54  	// Compute a number d_v for each vertex v in G,
    55  	// the number of neighbors of v that are not already in L.
    56  	// Initially, these numbers are just the degrees of the vertices.
    57  	dv := make(map[int64]int, len(nodes))
    58  	var (
    59  		maxDegree  int
    60  		neighbours = make(map[int64][]graph.Node)
    61  	)
    62  	for _, n := range nodes {
    63  		id := n.ID()
    64  		adj := graph.NodesOf(g.From(id))
    65  		neighbours[id] = adj
    66  		dv[id] = len(adj)
    67  		if len(adj) > maxDegree {
    68  			maxDegree = len(adj)
    69  		}
    70  	}
    71  
    72  	// Initialize an array D such that D[i] contains a list of the
    73  	// vertices v that are not already in L for which d_v = i.
    74  	d := make([][]graph.Node, maxDegree+1)
    75  	for _, n := range nodes {
    76  		deg := dv[n.ID()]
    77  		d[deg] = append(d[deg], n)
    78  	}
    79  
    80  	// Initialize k to 0.
    81  	k := 0
    82  	// Repeat n times:
    83  	s = []int{0}
    84  	for range nodes {
    85  		// Scan the array cells D[0], D[1], ... until
    86  		// finding an i for which D[i] is nonempty.
    87  		var (
    88  			i  int
    89  			di []graph.Node
    90  		)
    91  		for i, di = range d {
    92  			if len(di) != 0 {
    93  				break
    94  			}
    95  		}
    96  
    97  		// Set k to max(k,i).
    98  		if i > k {
    99  			k = i
   100  			s = append(s, make([]int, k-len(s)+1)...)
   101  		}
   102  
   103  		// Select a vertex v from D[i]. Add v to the
   104  		// beginning of L and remove it from D[i].
   105  		var v graph.Node
   106  		v, d[i] = di[len(di)-1], di[:len(di)-1]
   107  		l = append(l, v)
   108  		s[k]++
   109  		delete(dv, v.ID())
   110  
   111  		// For each neighbor w of v not already in L,
   112  		// subtract one from d_w and move w to the
   113  		// cell of D corresponding to the new value of d_w.
   114  		for _, w := range neighbours[v.ID()] {
   115  			dw, ok := dv[w.ID()]
   116  			if !ok {
   117  				continue
   118  			}
   119  			for i, n := range d[dw] {
   120  				if n.ID() == w.ID() {
   121  					d[dw][i], d[dw] = d[dw][len(d[dw])-1], d[dw][:len(d[dw])-1]
   122  					dw--
   123  					d[dw] = append(d[dw], w)
   124  					break
   125  				}
   126  			}
   127  			dv[w.ID()] = dw
   128  		}
   129  	}
   130  
   131  	return l, s
   132  }
   133  
   134  // BronKerbosch returns the set of maximal cliques of the undirected graph g.
   135  func BronKerbosch(g graph.Undirected) [][]graph.Node {
   136  	nodes := graph.NodesOf(g.Nodes())
   137  
   138  	// The algorithm used here is essentially BronKerbosch3 as described at
   139  	// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
   140  
   141  	p := set.NewNodesSize(len(nodes))
   142  	for _, n := range nodes {
   143  		p.Add(n)
   144  	}
   145  	x := set.NewNodes()
   146  	var bk bronKerbosch
   147  	order, _ := degeneracyOrdering(g)
   148  	ordered.Reverse(order)
   149  	for _, v := range order {
   150  		neighbours := graph.NodesOf(g.From(v.ID()))
   151  		nv := set.NewNodesSize(len(neighbours))
   152  		for _, n := range neighbours {
   153  			nv.Add(n)
   154  		}
   155  		bk.maximalCliquePivot(g, []graph.Node{v}, set.IntersectionOfNodes(p, nv), set.IntersectionOfNodes(x, nv))
   156  		p.Remove(v)
   157  		x.Add(v)
   158  	}
   159  	return bk
   160  }
   161  
   162  type bronKerbosch [][]graph.Node
   163  
   164  func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p, x set.Nodes) {
   165  	if len(p) == 0 && len(x) == 0 {
   166  		*bk = append(*bk, r)
   167  		return
   168  	}
   169  
   170  	neighbours := bk.choosePivotFrom(g, p, x)
   171  	nu := set.NewNodesSize(len(neighbours))
   172  	for _, n := range neighbours {
   173  		nu.Add(n)
   174  	}
   175  	for _, v := range p {
   176  		if nu.Has(v) {
   177  			continue
   178  		}
   179  		vid := v.ID()
   180  		neighbours := graph.NodesOf(g.From(vid))
   181  		nv := set.NewNodesSize(len(neighbours))
   182  		for _, n := range neighbours {
   183  			nv.Add(n)
   184  		}
   185  
   186  		var found bool
   187  		for _, n := range r {
   188  			if n.ID() == vid {
   189  				found = true
   190  				break
   191  			}
   192  		}
   193  		var sr []graph.Node
   194  		if !found {
   195  			sr = append(r[:len(r):len(r)], v)
   196  		}
   197  
   198  		bk.maximalCliquePivot(g, sr, set.IntersectionOfNodes(p, nv), set.IntersectionOfNodes(x, nv))
   199  		p.Remove(v)
   200  		x.Add(v)
   201  	}
   202  }
   203  
   204  func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighbors []graph.Node) {
   205  	// TODO(kortschak): Investigate the impact of pivot choice that maximises
   206  	// |p ⋂ neighbours(u)| as a function of input size. Until then, leave as
   207  	// compile time option.
   208  	if !tomitaTanakaTakahashi {
   209  		for _, n := range p {
   210  			return graph.NodesOf(g.From(n.ID()))
   211  		}
   212  		for _, n := range x {
   213  			return graph.NodesOf(g.From(n.ID()))
   214  		}
   215  		panic("bronKerbosch: empty set")
   216  	}
   217  
   218  	var (
   219  		max   = -1
   220  		pivot graph.Node
   221  	)
   222  	maxNeighbors := func(s set.Nodes) {
   223  	outer:
   224  		for _, u := range s {
   225  			nb := graph.NodesOf(g.From(u.ID()))
   226  			c := len(nb)
   227  			if c <= max {
   228  				continue
   229  			}
   230  			for n := range nb {
   231  				if _, ok := p[int64(n)]; ok {
   232  					continue
   233  				}
   234  				c--
   235  				if c <= max {
   236  					continue outer
   237  				}
   238  			}
   239  			max = c
   240  			pivot = u
   241  			neighbors = nb
   242  		}
   243  	}
   244  	maxNeighbors(p)
   245  	maxNeighbors(x)
   246  	if pivot == nil {
   247  		panic("bronKerbosch: empty set")
   248  	}
   249  	return neighbors
   250  }