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 }