示例#1
0
// 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
}
示例#2
0
// 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
}