diff options
| author | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
|---|---|---|
| committer | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
| commit | b5eb2866cfceb69b0d4dd4948273d679a884fbb2 (patch) | |
| tree | 1fdb61a7138642a1612bb201434e8ebda141cc8a /vendor/github.com/soniakeys/graph/random.go | |
| parent | 8de8e05c483c6b5f23b14742315f1860211dcef7 (diff) | |
| download | gdrive-b5eb2866cfceb69b0d4dd4948273d679a884fbb2.tar.bz2 | |
add Go dependencies by godep
see https://github.com/tools/godep
Diffstat (limited to 'vendor/github.com/soniakeys/graph/random.go')
| -rw-r--r-- | vendor/github.com/soniakeys/graph/random.go | 325 | 
1 files changed, 325 insertions, 0 deletions
| diff --git a/vendor/github.com/soniakeys/graph/random.go b/vendor/github.com/soniakeys/graph/random.go new file mode 100644 index 0000000..99f0445 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/random.go @@ -0,0 +1,325 @@ +// Copyright 2016 Sonia Keys +// License MIT: https://opensource.org/licenses/MIT + +package graph + +import ( +	"errors" +	"math" +	"math/rand" +	"time" +) + +// Euclidean generates a random simple graph on the Euclidean plane. +// +// Nodes are associated with coordinates uniformly distributed on a unit +// square.  Arcs are added between random nodes with a bias toward connecting +// nearer nodes. +// +// Unfortunately the function has a few "knobs". +// The returned graph will have order nNodes and arc size nArcs.  The affinity +// argument controls the bias toward connecting nearer nodes.  The function +// selects random pairs of nodes as a candidate arc then rejects the candidate +// if the nodes fail an affinity test.  Also parallel arcs are rejected. +// As more affine or denser graphs are requested, rejections increase, +// increasing run time.  The patience argument controls the number of arc +// rejections allowed before the function gives up and returns an error. +// Note that higher affinity will require more patience and that some +// combinations of nNodes and nArcs cannot be achieved with any amount of +// patience given that the returned graph must be simple. +// +// If Rand r is nil, the method creates a new source and generator for +// one-time use. +// +// Returned is a directed simple graph and associated positions indexed by +// node number. +// +// See also LabeledEuclidean. +func Euclidean(nNodes, nArcs int, affinity float64, patience int, r *rand.Rand) (g Directed, pos []struct{ X, Y float64 }, err error) { +	a := make(AdjacencyList, nNodes) // graph +	// generate random positions +	if r == nil { +		r = rand.New(rand.NewSource(time.Now().UnixNano())) +	} +	pos = make([]struct{ X, Y float64 }, nNodes) +	for i := range pos { +		pos[i].X = r.Float64() +		pos[i].Y = r.Float64() +	} +	// arcs +	var tooFar, dup int +arc: +	for i := 0; i < nArcs; { +		if tooFar == nArcs*patience { +			err = errors.New("affinity not found") +			return +		} +		if dup == nArcs*patience { +			err = errors.New("overcrowding") +			return +		} +		n1 := NI(r.Intn(nNodes)) +		var n2 NI +		for { +			n2 = NI(r.Intn(nNodes)) +			if n2 != n1 { // no graph loops +				break +			} +		} +		c1 := &pos[n1] +		c2 := &pos[n2] +		dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y) +		if dist*affinity > r.ExpFloat64() { // favor near nodes +			tooFar++ +			continue +		} +		for _, nb := range a[n1] { +			if nb == n2 { // no parallel arcs +				dup++ +				continue arc +			} +		} +		a[n1] = append(a[n1], n2) +		i++ +	} +	g = Directed{a} +	return +} + +// LabeledEuclidean generates a random simple graph on the Euclidean plane. +// +// Arc label values in the returned graph g are indexes into the return value +// wt.  Wt is the Euclidean distance between the from and to nodes of the arc. +// +// Otherwise the function arguments and return values are the same as for +// function Euclidean.  See Euclidean. +func LabeledEuclidean(nNodes, nArcs int, affinity float64, patience int, r *rand.Rand) (g LabeledDirected, pos []struct{ X, Y float64 }, wt []float64, err error) { +	a := make(LabeledAdjacencyList, nNodes) // graph +	wt = make([]float64, nArcs)             // arc weights +	// generate random positions +	if r == nil { +		r = rand.New(rand.NewSource(time.Now().UnixNano())) +	} +	pos = make([]struct{ X, Y float64 }, nNodes) +	for i := range pos { +		pos[i].X = r.Float64() +		pos[i].Y = r.Float64() +	} +	// arcs +	var tooFar, dup int +arc: +	for i := 0; i < nArcs; { +		if tooFar == nArcs*patience { +			err = errors.New("affinity not found") +			return +		} +		if dup == nArcs*patience { +			err = errors.New("overcrowding") +			return +		} +		n1 := NI(r.Intn(nNodes)) +		var n2 NI +		for { +			n2 = NI(r.Intn(nNodes)) +			if n2 != n1 { // no graph loops +				break +			} +		} +		c1 := &pos[n1] +		c2 := &pos[n2] +		dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y) +		if dist*affinity > r.ExpFloat64() { // favor near nodes +			tooFar++ +			continue +		} +		for _, nb := range a[n1] { +			if nb.To == n2 { // no parallel arcs +				dup++ +				continue arc +			} +		} +		wt[i] = dist +		a[n1] = append(a[n1], Half{n2, LI(i)}) +		i++ +	} +	g = LabeledDirected{a} +	return +} + +// Geometric generates a random geometric graph (RGG) on the Euclidean plane. +// +// An RGG is an undirected simple graph.  Nodes are associated with coordinates +// uniformly distributed on a unit square.  Edges are added between all nodes +// falling within a specified distance or radius of each other. +// +// The resulting number of edges is somewhat random but asymptotically +// approaches m = πr²n²/2.   The method accumulates and returns the actual +// number of edges constructed. +// +// If Rand r is nil, the method creates a new source and generator for +// one-time use. +// +// See also LabeledGeometric. +func Geometric(nNodes int, radius float64, r *rand.Rand) (g Undirected, pos []struct{ X, Y float64 }, m int) { +	// Expected degree is approximately nπr². +	a := make(AdjacencyList, nNodes) +	if r == nil { +		r = rand.New(rand.NewSource(time.Now().UnixNano())) +	} +	pos = make([]struct{ X, Y float64 }, nNodes) +	for i := range pos { +		pos[i].X = r.Float64() +		pos[i].Y = r.Float64() +	} +	for u, up := range pos { +		for v := u + 1; v < len(pos); v++ { +			vp := pos[v] +			if math.Hypot(up.X-vp.X, up.Y-vp.Y) < radius { +				a[u] = append(a[u], NI(v)) +				a[v] = append(a[v], NI(u)) +				m++ +			} +		} +	} +	g = Undirected{a} +	return +} + +// LabeledGeometric generates a random geometric graph (RGG) on the Euclidean +// plane. +// +// Edge label values in the returned graph g are indexes into the return value +// wt.  Wt is the Euclidean distance between nodes of the edge.  The graph +// size m is len(wt). +// +// See Geometric for additional description. +func LabeledGeometric(nNodes int, radius float64, r *rand.Rand) (g LabeledUndirected, pos []struct{ X, Y float64 }, wt []float64) { +	a := make(LabeledAdjacencyList, nNodes) +	if r == nil { +		r = rand.New(rand.NewSource(time.Now().UnixNano())) +	} +	pos = make([]struct{ X, Y float64 }, nNodes) +	for i := range pos { +		pos[i].X = r.Float64() +		pos[i].Y = r.Float64() +	} +	for u, up := range pos { +		for v := u + 1; v < len(pos); v++ { +			vp := pos[v] +			if w := math.Hypot(up.X-vp.X, up.Y-vp.Y); w < radius { +				a[u] = append(a[u], Half{NI(v), LI(len(wt))}) +				a[v] = append(a[v], Half{NI(u), LI(len(wt))}) +				wt = append(wt, w) +			} +		} +	} +	g = LabeledUndirected{a} +	return +} + +// KroneckerDirected generates a Kronecker-like random directed graph. +// +// The returned graph g is simple and has no isolated nodes but is not +// necessarily fully connected.  The number of of nodes will be <= 2^scale, +// and will be near 2^scale for typical values of arcFactor, >= 2. +// ArcFactor * 2^scale arcs are generated, although loops and duplicate arcs +// are rejected. +// +// If Rand r is nil, the method creates a new source and generator for +// one-time use. +// +// Return value ma is the number of arcs retained in the result graph. +func KroneckerDirected(scale uint, arcFactor float64, r *rand.Rand) (g Directed, ma int) { +	a, m := kronecker(scale, arcFactor, true, r) +	return Directed{a}, m +} + +// KroneckerUndirected generates a Kronecker-like random undirected graph. +// +// The returned graph g is simple and has no isolated nodes but is not +// necessarily fully connected.  The number of of nodes will be <= 2^scale, +// and will be near 2^scale for typical values of edgeFactor, >= 2. +// EdgeFactor * 2^scale edges are generated, although loops and duplicate edges +// are rejected. +// +// If Rand r is nil, the method creates a new source and generator for +// one-time use. +// +// Return value m is the true number of edges--not arcs--retained in the result +// graph. +func KroneckerUndirected(scale uint, edgeFactor float64, r *rand.Rand) (g Undirected, m int) { +	al, s := kronecker(scale, edgeFactor, false, r) +	return Undirected{al}, s +} + +// Styled after the Graph500 example code.  Not well tested currently. +// Graph500 example generates undirected only.  No idea if the directed variant +// here is meaningful or not. +// +// note mma returns arc size ma for dir=true, but returns size m for dir=false +func kronecker(scale uint, edgeFactor float64, dir bool, r *rand.Rand) (g AdjacencyList, mma int) { +	if r == nil { +		r = rand.New(rand.NewSource(time.Now().UnixNano())) +	} +	N := NI(1 << scale)                  // node extent +	M := int(edgeFactor*float64(N) + .5) // number of arcs/edges to generate +	a, b, c := 0.57, 0.19, 0.19          // initiator probabilities +	ab := a + b +	cNorm := c / (1 - ab) +	aNorm := a / ab +	ij := make([][2]NI, M) +	var bm Bits +	var nNodes int +	for k := range ij { +		var i, j NI +		for b := NI(1); b < N; b <<= 1 { +			if r.Float64() > ab { +				i |= b +				if r.Float64() > cNorm { +					j |= b +				} +			} else if r.Float64() > aNorm { +				j |= b +			} +		} +		if bm.Bit(i) == 0 { +			bm.SetBit(i, 1) +			nNodes++ +		} +		if bm.Bit(j) == 0 { +			bm.SetBit(j, 1) +			nNodes++ +		} +		r := r.Intn(k + 1) // shuffle edges as they are generated +		ij[k] = ij[r] +		ij[r] = [2]NI{i, j} +	} +	p := r.Perm(nNodes) // mapping to shuffle IDs of non-isolated nodes +	px := 0 +	rn := make([]NI, N) +	for i := range rn { +		if bm.Bit(NI(i)) == 1 { +			rn[i] = NI(p[px]) // fill lookup table +			px++ +		} +	} +	g = make(AdjacencyList, nNodes) +ij: +	for _, e := range ij { +		if e[0] == e[1] { +			continue // skip loops +		} +		ri, rj := rn[e[0]], rn[e[1]] +		for _, nb := range g[ri] { +			if nb == rj { +				continue ij // skip parallel edges +			} +		} +		g[ri] = append(g[ri], rj) +		mma++ +		if !dir { +			g[rj] = append(g[rj], ri) +		} +	} +	return +} | 
