gonum.org/v1/gonum@v0.14.0/graph/graphs/gen/holme_kim.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 gen
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  
    11  	"golang.org/x/exp/rand"
    12  
    13  	"gonum.org/v1/gonum/graph"
    14  	"gonum.org/v1/gonum/graph/simple"
    15  	"gonum.org/v1/gonum/stat/sampleuv"
    16  )
    17  
    18  // TunableClusteringScaleFree constructs a subgraph in the destination, dst, of order n.
    19  // The graph is constructed successively starting from an m order graph with one node
    20  // having degree m-1. At each iteration of graph addition, one node is added with m
    21  // additional edges joining existing nodes with probability proportional to the nodes'
    22  // degrees. The edges are formed as a triad with probability, p.
    23  // If src is not nil it is used as the random source, otherwise rand.Float64 and
    24  // rand.Intn are used for the random number generators.
    25  //
    26  // The algorithm is essentially as described in http://arxiv.org/abs/cond-mat/0110452.
    27  func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64, src rand.Source) error {
    28  	if p < 0 || p > 1 {
    29  		return fmt.Errorf("gen: bad probability: p=%v", p)
    30  	}
    31  	if n <= m {
    32  		return fmt.Errorf("gen: n <= m: n=%v m=%d", n, m)
    33  	}
    34  
    35  	var (
    36  		rnd  func() float64
    37  		rndN func(int) int
    38  	)
    39  	if src == nil {
    40  		rnd = rand.Float64
    41  		rndN = rand.Intn
    42  	} else {
    43  		r := rand.New(src)
    44  		rnd = r.Float64
    45  		rndN = r.Intn
    46  	}
    47  
    48  	// Initial condition.
    49  	wt := make([]float64, n)
    50  	id := make([]int64, n)
    51  	for u := 0; u < m; u++ {
    52  		un := dst.NewNode()
    53  		dst.AddNode(un)
    54  		id[u] = un.ID()
    55  		// We need to give equal probability for
    56  		// adding the first generation of edges.
    57  		wt[u] = 1
    58  	}
    59  	ws := sampleuv.NewWeighted(wt, src)
    60  	for i := range wt {
    61  		// These weights will organically grow
    62  		// after the first growth iteration.
    63  		wt[i] = 0
    64  	}
    65  
    66  	// Growth.
    67  	for v := m; v < n; v++ {
    68  		vn := dst.NewNode()
    69  		dst.AddNode(vn)
    70  		id[v] = vn.ID()
    71  		var u int
    72  	pa:
    73  		for i := 0; i < m; i++ {
    74  			// Triad formation.
    75  			if i != 0 && rnd() < p {
    76  				// TODO(kortschak): Decide whether the node
    77  				// order in this input to permute should be
    78  				// sorted first to allow repeatable runs.
    79  				for _, w := range permute(graph.NodesOf(dst.From(id[u])), rndN) {
    80  					wid := w.ID()
    81  					if wid == id[v] || dst.HasEdgeBetween(wid, id[v]) {
    82  						continue
    83  					}
    84  
    85  					dst.SetEdge(dst.NewEdge(w, vn))
    86  					wt[wid]++
    87  					wt[v]++
    88  					continue pa
    89  				}
    90  			}
    91  
    92  			// Preferential attachment.
    93  			for {
    94  				var ok bool
    95  				u, ok = ws.Take()
    96  				if !ok {
    97  					return errors.New("gen: depleted distribution")
    98  				}
    99  				if u == v || dst.HasEdgeBetween(id[u], id[v]) {
   100  					continue
   101  				}
   102  				dst.SetEdge(dst.NewEdge(dst.Node(id[u]), vn))
   103  				wt[u]++
   104  				wt[v]++
   105  				break
   106  			}
   107  		}
   108  
   109  		ws.ReweightAll(wt)
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func permute(n []graph.Node, rnd func(int) int) []graph.Node {
   116  	for i := range n[:len(n)-1] {
   117  		j := rnd(len(n)-i) + i
   118  		n[i], n[j] = n[j], n[i]
   119  	}
   120  	return n
   121  }
   122  
   123  // PreferentialAttachment constructs a graph in the destination, dst, of order n.
   124  // The graph is constructed successively starting from an m order graph with one
   125  // node having degree m-1. At each iteration of graph addition, one node is added
   126  // with m additional edges joining existing nodes with probability proportional
   127  // to the nodes' degrees. If src is not nil it is used as the random source,
   128  // otherwise rand.Float64 is used for the random number generator.
   129  //
   130  // The algorithm is essentially as described in http://arxiv.org/abs/cond-mat/0110452
   131  // after 10.1126/science.286.5439.509.
   132  func PreferentialAttachment(dst graph.UndirectedBuilder, n, m int, src rand.Source) error {
   133  	if n <= m {
   134  		return fmt.Errorf("gen: n <= m: n=%v m=%d", n, m)
   135  	}
   136  
   137  	// Initial condition.
   138  	wt := make([]float64, n)
   139  	for u := 0; u < m; u++ {
   140  		if dst.Node(int64(u)) == nil {
   141  			dst.AddNode(simple.Node(u))
   142  		}
   143  		// We need to give equal probability for
   144  		// adding the first generation of edges.
   145  		wt[u] = 1
   146  	}
   147  	ws := sampleuv.NewWeighted(wt, src)
   148  	for i := range wt {
   149  		// These weights will organically grow
   150  		// after the first growth iteration.
   151  		wt[i] = 0
   152  	}
   153  
   154  	// Growth.
   155  	for v := m; v < n; v++ {
   156  		for i := 0; i < m; i++ {
   157  			// Preferential attachment.
   158  			u, ok := ws.Take()
   159  			if !ok {
   160  				return errors.New("gen: depleted distribution")
   161  			}
   162  			dst.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
   163  			wt[u]++
   164  			wt[v]++
   165  		}
   166  		ws.ReweightAll(wt)
   167  	}
   168  
   169  	return nil
   170  }