// NavigableSmallWorld constructs an N-dimensional grid with guaranteed local connectivity // and random long-range connectivity in the destination, dst. The dims parameters specifies // the length of each of the N dimensions, p defines the Manhattan distance between local // nodes, and q defines the number of out-going long-range connections from each node. Long- // range connections are made with a probability proportional to |d(u,v)|^-r where d is the // Manhattan distance between non-local nodes. // // The algorithm is essentially as described on p4 of http://www.cs.cornell.edu/home/kleinber/swn.pdf. func NavigableSmallWorld(dst GraphBuilder, dims []int, p, q int, r float64, src *rand.Rand) (err error) { if p < 1 { return fmt.Errorf("gen: bad local distance: p=%v", p) } if q < 0 { return fmt.Errorf("gen: bad distant link count: q=%v", q) } if r < 0 { return fmt.Errorf("gen: bad decay constant: r=%v", r) } n := 1 for _, d := range dims { n *= d } for i := 0; i < n; i++ { if !dst.Has(simple.Node(i)) { dst.AddNode(simple.Node(i)) } } hasEdge := dst.HasEdgeBetween d, isDirected := dst.(graph.Directed) if isDirected { hasEdge = d.HasEdgeFromTo } locality := make([]int, len(dims)) for i := range locality { locality[i] = p*2 + 1 } iterateOver(dims, func(u []int) { uid := idFrom(u, dims) iterateOver(locality, func(delta []int) { d := manhattanDelta(u, delta, dims, -p) if d == 0 || d > p { return } vid := idFromDelta(u, delta, dims, -p) e := simple.Edge{F: simple.Node(uid), T: simple.Node(vid), W: 1} if uid > vid { e.F, e.T = e.T, e.F } if !hasEdge(e.From(), e.To()) { dst.SetEdge(e) } if !isDirected { return } e.F, e.T = e.T, e.F if !hasEdge(e.From(), e.To()) { dst.SetEdge(e) } }) }) defer func() { r := recover() if r != nil { if r != "depleted distribution" { panic(r) } err = errors.New("depleted distribution") } }() w := make([]float64, n) ws := sample.NewWeighted(w, src) iterateOver(dims, func(u []int) { uid := idFrom(u, dims) iterateOver(dims, func(v []int) { d := manhattanBetween(u, v) if d <= p { return } w[idFrom(v, dims)] = math.Pow(float64(d), -r) }) ws.ReweightAll(w) for i := 0; i < q; i++ { vid, ok := ws.Take() if !ok { panic("depleted distribution") } e := simple.Edge{F: simple.Node(uid), T: simple.Node(vid), W: 1} if !isDirected && uid > vid { e.F, e.T = e.T, e.F } if !hasEdge(e.From(), e.To()) { dst.SetEdge(e) } } for i := range w { w[i] = 0 } }) return nil }
// SmallWorldsBB constructs a small worlds graph of order n in the destination, dst. // Node degree is specified by d and edge replacement by the probability, p. // If src is not nil it is used as the random source, otherwise rand.Float64 is used. // The graph is constructed in O(nd) time. // // The algorithm used is described in http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error { if d < 1 || d > (n-1)/2 { return fmt.Errorf("gen: bad degree: d=%d", d) } if p == 0 { return nil } if p < 0 || p >= 1 { return fmt.Errorf("gen: bad replacement: p=%v", p) } var ( rnd func() float64 rndN func(int) int ) if src == nil { rnd = rand.Float64 rndN = rand.Intn } else { rnd = src.Float64 rndN = src.Intn } hasEdge := dst.HasEdgeBetween dg, isDirected := dst.(graph.Directed) if isDirected { hasEdge = dg.HasEdgeFromTo } for i := 0; i < n; i++ { if !dst.Has(simple.Node(i)) { dst.AddNode(simple.Node(i)) } } nChoose2 := (n - 1) * n / 2 lp := math.Log(1 - p) // Add forward edges for all graphs. k := int(math.Log(1-rnd()) / lp) m := 0 replace := make(map[int]int) for v := 0; v < n; v++ { for i := 1; i <= d; i++ { if k > 0 { j := v*(v-1)/2 + (v+i)%n ej := simple.Edge{W: 1} ej.T, ej.F = edgeNodesFor(j) if !hasEdge(ej.From(), ej.To()) { dst.SetEdge(ej) } k-- m++ em := simple.Edge{W: 1} em.T, em.F = edgeNodesFor(m) if !hasEdge(em.From(), em.To()) { replace[j] = m } else { replace[j] = replace[m] } } else { k = int(math.Log(1-rnd()) / lp) } } } for i := m + 1; i <= n*d && i < nChoose2; i++ { r := rndN(nChoose2-i) + i er := simple.Edge{W: 1} er.T, er.F = edgeNodesFor(r) if !hasEdge(er.From(), er.To()) { dst.SetEdge(er) } else { er.T, er.F = edgeNodesFor(replace[r]) if !hasEdge(er.From(), er.To()) { dst.SetEdge(er) } } ei := simple.Edge{W: 1} ei.T, ei.F = edgeNodesFor(i) if !hasEdge(ei.From(), ei.To()) { replace[r] = i } else { replace[r] = replace[i] } } // Add backward edges for directed graphs. if !isDirected { return nil } k = int(math.Log(1-rnd()) / lp) m = 0 replace = make(map[int]int) for v := 0; v < n; v++ { for i := 1; i <= d; i++ { if k > 0 { j := v*(v-1)/2 + (v+i)%n ej := simple.Edge{W: 1} ej.F, ej.T = edgeNodesFor(j) if !hasEdge(ej.From(), ej.To()) { dst.SetEdge(ej) } k-- m++ if !hasEdge(edgeNodesFor(m)) { replace[j] = m } else { replace[j] = replace[m] } } else { k = int(math.Log(1-rnd()) / lp) } } } for i := m + 1; i <= n*d && i < nChoose2; i++ { r := rndN(nChoose2-i) + i er := simple.Edge{W: 1} er.F, er.T = edgeNodesFor(r) if !hasEdge(er.From(), er.To()) { dst.SetEdge(er) } else { er.F, er.T = edgeNodesFor(replace[r]) if !hasEdge(er.From(), er.To()) { dst.SetEdge(er) } } if !hasEdge(edgeNodesFor(i)) { replace[r] = i } else { replace[r] = replace[i] } } return nil }