Skip to content

Commit

Permalink
Merge c2cafa6 into f098207
Browse files Browse the repository at this point in the history
  • Loading branch information
kortschak committed Oct 21, 2018
2 parents f098207 + c2cafa6 commit 82ef118
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 113 deletions.
152 changes: 81 additions & 71 deletions graph/graphs/gen/batagelj_brandes.go
Expand Up @@ -14,14 +14,13 @@ import (
"golang.org/x/exp/rand"

"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/simple"
)

// Gnp constructs a Gilbert’s model graph in the destination, dst, of order n. Edges
// Gnp constructs a Gilbert’s model subgraph in the destination, dst, of order n. Edges
// between nodes are formed with 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(n+m) time where m is the number of edges added.
func Gnp(dst GraphBuilder, n int, p float64, src rand.Source) error {
func Gnp(dst graph.Builder, n int, p float64, src rand.Source) error {
if p == 0 {
return nil
}
Expand All @@ -35,10 +34,11 @@ func Gnp(dst GraphBuilder, n int, p float64, src rand.Source) error {
r = rand.New(src).Float64
}

for i := 0; i < n; i++ {
if dst.Node(int64(i)) == nil {
dst.AddNode(simple.Node(i))
}
nodes := make([]graph.Node, n)
for i := range nodes {
u := dst.NewNode()
dst.AddNode(u)
nodes[i] = u
}

lp := math.Log(1 - p)
Expand All @@ -51,7 +51,7 @@ func Gnp(dst GraphBuilder, n int, p float64, src rand.Source) error {
v++
}
if v < n {
dst.SetEdge(simple.Edge{F: simple.Node(w), T: simple.Node(v)})
dst.SetEdge(dst.NewEdge(nodes[w], nodes[v]))
}
}

Expand All @@ -66,24 +66,14 @@ func Gnp(dst GraphBuilder, n int, p float64, src rand.Source) error {
v++
}
if v < n {
dst.SetEdge(simple.Edge{F: simple.Node(v), T: simple.Node(w)})
dst.SetEdge(dst.NewEdge(nodes[v], nodes[w]))
}
}

return nil
}

// edgeNodesFor returns the pair of nodes for the ith edge in a simple
// undirected graph. The pair is returned such that w.ID < v.ID.
func edgeNodesFor(i int) (v, w simple.Node) {
// This is an algebraic simplification of the expressions described
// on p3 of http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
v = simple.Node(0.5 + math.Sqrt(float64(1+8*i))/2)
w = simple.Node(i) - v*(v-1)/2
return v, w
}

// Gnm constructs a Erdős-Rényi model graph in the destination, dst, of
// Gnm constructs a Erdős-Rényi model subgraph in the destination, dst, of
// order n and size m. If src is not nil it is used as the random source,
// otherwise rand.Intn is used. The graph is constructed in O(m) expected
// time for m ≤ (n choose 2)/2.
Expand Down Expand Up @@ -111,19 +101,19 @@ func Gnm(dst GraphBuilder, n, m int, src rand.Source) error {
rnd = rand.New(src).Intn
}

for i := 0; i < n; i++ {
if dst.Node(int64(i)) == nil {
dst.AddNode(simple.Node(i))
}
nodes := make([]graph.Node, n)
for i := range nodes {
u := dst.NewNode()
dst.AddNode(u)
nodes[i] = u
}

// Add forward edges for all graphs.
for i := 0; i < m; i++ {
for {
v, w := edgeNodesFor(rnd(nChoose2))
e := simple.Edge{F: w, T: v}
if !hasEdge(e.F.ID(), e.T.ID()) {
dst.SetEdge(e)
v, w := edgeNodesFor(rnd(nChoose2), nodes)
if !hasEdge(w.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(w, v))
break
}
}
Expand All @@ -135,10 +125,9 @@ func Gnm(dst GraphBuilder, n, m int, src rand.Source) error {
}
for i := 0; i < m; i++ {
for {
v, w := edgeNodesFor(rnd(nChoose2))
e := simple.Edge{F: v, T: w}
if !hasEdge(e.F.ID(), e.T.ID()) {
dst.SetEdge(e)
v, w := edgeNodesFor(rnd(nChoose2), nodes)
if !hasEdge(v.ID(), w.ID()) {
dst.SetEdge(dst.NewEdge(v, w))
break
}
}
Expand All @@ -147,7 +136,7 @@ func Gnm(dst GraphBuilder, n, m int, src rand.Source) error {
return nil
}

// SmallWorldsBB constructs a small worlds graph of order n in the destination, dst.
// SmallWorldsBB constructs a small worlds subgraph 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.
Expand All @@ -158,6 +147,9 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
return fmt.Errorf("gen: bad degree: d=%d", d)
}
if p == 0 {
for i := 0; i < n; i++ {
dst.AddNode(dst.NewNode())
}
return nil
}
if p < 0 || p >= 1 {
Expand All @@ -182,10 +174,11 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
hasEdge = dg.HasEdgeFromTo
}

for i := 0; i < n; i++ {
if dst.Node(int64(i)) == nil {
dst.AddNode(simple.Node(i))
}
nodes := make([]graph.Node, n)
for i := range nodes {
u := dst.NewNode()
dst.AddNode(u)
nodes[i] = u
}

nChoose2 := (n - 1) * n / 2
Expand All @@ -200,16 +193,24 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
for i := 1; i <= d; i++ {
if k > 0 {
j := v*(v-1)/2 + (v+i)%n
var ej simple.Edge
ej.T, ej.F = edgeNodesFor(j)
if !hasEdge(ej.From().ID(), ej.To().ID()) {
dst.SetEdge(ej)
if v, u := edgeNodesFor(j, nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
}
k--
m++
var em simple.Edge
em.T, em.F = edgeNodesFor(m)
if !hasEdge(em.From().ID(), em.To().ID()) {

// For small graphs, m may be an
// edge that has an end that is
// not in the subgraph.
if m >= nChoose2 {
// Since m is monotonically
// increasing, no m edges from
// here on are valid, so don't
// add them to replace.
continue
}

if v, u := edgeNodesFor(m, nodes); !hasEdge(u.ID(), v.ID()) {
replace[j] = m
} else {
replace[j] = replace[m]
Expand All @@ -221,19 +222,12 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
}
for i := m + 1; i <= n*d && i < nChoose2; i++ {
r := rndN(nChoose2-i) + i
var er simple.Edge
er.T, er.F = edgeNodesFor(r)
if !hasEdge(er.From().ID(), er.To().ID()) {
dst.SetEdge(er)
} else {
er.T, er.F = edgeNodesFor(replace[r])
if !hasEdge(er.From().ID(), er.To().ID()) {
dst.SetEdge(er)
}
if v, u := edgeNodesFor(r, nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
} else if v, u = edgeNodesFor(replace[r], nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
}
var ei simple.Edge
ei.T, ei.F = edgeNodesFor(i)
if !hasEdge(ei.From().ID(), ei.To().ID()) {
if v, u := edgeNodesFor(i, nodes); !hasEdge(u.ID(), v.ID()) {
replace[r] = i
} else {
replace[r] = replace[i]
Expand All @@ -251,14 +245,24 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
for i := 1; i <= d; i++ {
if k > 0 {
j := v*(v-1)/2 + (v+i)%n
var ej simple.Edge
ej.F, ej.T = edgeNodesFor(j)
if !hasEdge(ej.From().ID(), ej.To().ID()) {
dst.SetEdge(ej)
if u, v := edgeNodesFor(j, nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
}
k--
m++
if u, v := edgeNodesFor(m); !hasEdge(u.ID(), v.ID()) {

// For small graphs, m may be an
// edge that has an end that is
// not in the subgraph.
if m >= nChoose2 {
// Since m is monotonically
// increasing, no m edges from
// here on are valid, so don't
// add them to replace.
continue
}

if u, v := edgeNodesFor(m, nodes); !hasEdge(u.ID(), v.ID()) {
replace[j] = m
} else {
replace[j] = replace[m]
Expand All @@ -270,17 +274,12 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
}
for i := m + 1; i <= n*d && i < nChoose2; i++ {
r := rndN(nChoose2-i) + i
var er simple.Edge
er.F, er.T = edgeNodesFor(r)
if !hasEdge(er.From().ID(), er.To().ID()) {
dst.SetEdge(er)
} else {
er.F, er.T = edgeNodesFor(replace[r])
if !hasEdge(er.From().ID(), er.To().ID()) {
dst.SetEdge(er)
}
if u, v := edgeNodesFor(r, nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
} else if u, v = edgeNodesFor(replace[r], nodes); !hasEdge(u.ID(), v.ID()) {
dst.SetEdge(dst.NewEdge(u, v))
}
if u, v := edgeNodesFor(i); !hasEdge(u.ID(), v.ID()) {
if u, v := edgeNodesFor(i, nodes); !hasEdge(u.ID(), v.ID()) {
replace[r] = i
} else {
replace[r] = replace[i]
Expand All @@ -290,6 +289,17 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src rand.Source) error
return nil
}

// edgeNodesFor returns the pair of nodes for the ith edge in a simple
// undirected graph. The pair is returned such that the index of w in
// nodes is less than the index of v in nodes.
func edgeNodesFor(i int, nodes []graph.Node) (v, w graph.Node) {
// This is an algebraic simplification of the expressions described
// on p3 of http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
vi := int(0.5 + math.Sqrt(float64(1+8*i))/2)
wi := i - vi*(vi-1)/2
return nodes[vi], nodes[wi]
}

// Multigraph generators.

// PowerLaw constructs a power-law degree graph by preferential attachment in dst
Expand Down
30 changes: 30 additions & 0 deletions graph/graphs/gen/batagelj_brandes_test.go
Expand Up @@ -56,10 +56,15 @@ func TestGnpUndirected(t *testing.T) {
for n := 2; n <= 20; n++ {
for p := 0.; p <= 1; p += 0.1 {
g := &gnUndirected{UndirectedBuilder: simple.NewUndirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := Gnp(g, n, p, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, p=%v: %v", n, p, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, p=%v", n, p)
}
if g.addBackwards {
t.Errorf("edge added with From.ID > To.ID: n=%d, p=%v", n, p)
}
Expand All @@ -77,10 +82,15 @@ func TestGnpDirected(t *testing.T) {
for n := 2; n <= 20; n++ {
for p := 0.; p <= 1; p += 0.1 {
g := &gnDirected{DirectedBuilder: simple.NewDirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := Gnp(g, n, p, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, p=%v: %v", n, p, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, p=%v", n, p)
}
if g.addSelfLoop {
t.Errorf("unexpected self edge: n=%d, p=%v", n, p)
}
Expand All @@ -96,10 +106,15 @@ func TestGnmUndirected(t *testing.T) {
nChoose2 := (n - 1) * n / 2
for m := 0; m <= nChoose2; m++ {
g := &gnUndirected{UndirectedBuilder: simple.NewUndirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := Gnm(g, n, m, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, m=%d: %v", n, m, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, m=%d", n, m)
}
if g.addBackwards {
t.Errorf("edge added with From.ID > To.ID: n=%d, m=%d", n, m)
}
Expand All @@ -118,10 +133,15 @@ func TestGnmDirected(t *testing.T) {
nChoose2 := (n - 1) * n / 2
for m := 0; m <= nChoose2*2; m++ {
g := &gnDirected{DirectedBuilder: simple.NewDirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := Gnm(g, n, m, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, m=%d: %v", n, m, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, m=%d", n, m)
}
if g.addSelfLoop {
t.Errorf("unexpected self edge: n=%d, m=%d", n, m)
}
Expand All @@ -137,10 +157,15 @@ func TestSmallWorldsBBUndirected(t *testing.T) {
for d := 1; d <= (n-1)/2; d++ {
for p := 0.; p < 1; p += 0.1 {
g := &gnUndirected{UndirectedBuilder: simple.NewUndirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := SmallWorldsBB(g, n, d, p, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, d=%d, p=%v: %v", n, d, p, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, d=%d, p=%v", n, d, p)
}
if g.addBackwards {
t.Errorf("edge added with From.ID > To.ID: n=%d, d=%d, p=%v", n, d, p)
}
Expand All @@ -160,10 +185,15 @@ func TestSmallWorldsBBDirected(t *testing.T) {
for d := 1; d <= (n-1)/2; d++ {
for p := 0.; p < 1; p += 0.1 {
g := &gnDirected{DirectedBuilder: simple.NewDirectedGraph()}
orig := g.NewNode()
g.AddNode(orig)
err := SmallWorldsBB(g, n, d, p, nil)
if err != nil {
t.Fatalf("unexpected error: n=%d, d=%d, p=%v: %v", n, d, p, err)
}
if g.From(orig.ID()).Len() != 0 {
t.Errorf("edge added from already existing node: n=%d, d=%d, p=%v", n, d, p)
}
if g.addSelfLoop {
t.Errorf("unexpected self edge: n=%d, d=%d, p=%v", n, d, p)
}
Expand Down
1 change: 0 additions & 1 deletion graph/graphs/gen/gen.go
Expand Up @@ -8,7 +8,6 @@ import "gonum.org/v1/gonum/graph"

// GraphBuilder is a graph that can have nodes and edges added.
type GraphBuilder interface {
Node(id int64) graph.Node
HasEdgeBetween(xid, yid int64) bool
graph.Builder
}
Expand Down

0 comments on commit 82ef118

Please sign in to comment.