diff --git a/bipart.go b/bipart.go index c34520f..566157e 100644 --- a/bipart.go +++ b/bipart.go @@ -19,7 +19,8 @@ func Bipartition(g Iterator) (part []int, ok bool) { colors[v] = white whiteCount++ for queue := []int{v}; len(queue) > 0; { - v, queue = queue[0], queue[1:] + v := queue[0] + queue = queue[1:] if g.Visit(v, func(w int, _ int64) (skip bool) { switch { case colors[w] != none: diff --git a/build/build.go b/build/build.go index 4b6d903..8246e5f 100644 --- a/build/build.go +++ b/build/build.go @@ -23,11 +23,11 @@ // to turn on caching for any component. This gives constant time // performance for all basic operations on that component. // -// Example usage +// Tutorial // -// The package examples show how to build graphs from standard components -// using composition and filtering. They also demonstrate how to apply -// a cost function to a virtual graph. +// The Euclid and Maxflow examples show how to build graphs from +// standard components using composition and filtering. They also +// demonstrate how to apply a cost function to a virtual graph. // package build @@ -41,21 +41,25 @@ import ( // by composing and filtering a set of standard graphs, or by writing // functions that describe the edges of a graph. type Virtual struct { + // The `order` field is, in fact, a constant function. + // It returns the number of vertices in the graph. order int - // The edge and cost functions define a weighted graph without self-loops. + + // The `edge` and `cost` functions define a weighted graph without self-loops. // // • edge(v, w) returns true whenever (v, w) belongs to the graph; // the value is disregarded when v == w. // - // • cost(v, w) returns the cost of (v, w); the value is disregarded - // when edge(v, w) is false. + // • cost(v, w) returns the cost of (v, w); + // the value is disregarded when edge(v, w) is false. + // edge func(v, w int) bool cost func(v, w int) int64 - // These functions can be used to improve performance. - // They MUST BE CONSISTENT with edge and cost. - // The generic() factory method contains a basic implementation. - // The Consistent test function should be used to check compliance. + // The `degree` and `visit` functions can be used to improve performance. + // They MUST BE CONSISTENT with edge and cost. If not implemented, + // the `generic` or `generic0` implementation is used instead. + // The `Consistent` test function should be used to check compliance. // // • degree(v) returns the outdegree of vertex v. // @@ -64,12 +68,13 @@ type Virtual struct { // If a call to do returns true, visit MUST ABORT the iteration // and return true; if successful it should return false. // Precondition: a ≥ 0. + // degree func(v int) int visit func(v int, a int, do func(w int, c int64) (skip bool)) (aborted bool) } // FilterFunc is a function that tells if there is a directed edge from v to w. -// The nil value represents an edge functions that always returns true. +// The nil value represents an edge function that always returns true. type FilterFunc func(v, w int) bool // CostFunc is a function that computes the cost of an edge from v to w. @@ -107,7 +112,7 @@ func max(m, n int) int { return m } -// null is the null graph; a graph of order 0. +// null is the null graph; a graph with no vertices. var null = new(Virtual) // singleton returns a graph with one vertex. @@ -126,7 +131,7 @@ func edge() *Virtual { g := &Virtual{ order: 2, cost: zero, - edge: func(v, w int) bool { return v != w }, + edge: alwaysEdge, degree: degreeOne, } g.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) { @@ -260,17 +265,16 @@ func Generic(n int, edge FilterFunc) *Virtual { return generic0(n, edge) } -// Specific returns a cached copy of g with constant time performance -// for all basic operations. It uses space proportional to -// the size of the graph. +// Specific returns a cached copy of g with constant time performance for +// all basic operations. It uses space proportional to the size of the graph. // // This function does not accept multigraphs and graphs with self-loops. func Specific(g graph.Iterator) *Virtual { - stats := graph.Check(g) + h := graph.Sort(g) + stats := graph.Check(h) if stats.Multi != 0 || stats.Loops != 0 { panic("Virtual doesn't support multiple edges or self-loops") } - h := graph.Sort(g) res := &Virtual{ order: h.Order(), edge: h.Edge, @@ -282,7 +286,7 @@ func Specific(g graph.Iterator) *Virtual { return res } res.cost = func(v, w int) (cost int64) { - if !res.edge(v, w) { + if !h.Edge(v, w) { return 0 } h.VisitFrom(v, w, func(w int, c int64) (skip bool) { @@ -353,10 +357,7 @@ func (g *Virtual) Complement() *Virtual { return singleton() } res := generic0(n, func(v, w int) (edge bool) { - if v != w { - return !g.edge(v, w) - } - return + return v != w && !g.edge(v, w) }) res.degree = func(v int) int { return n - 1 - g.degree(v) } res.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) { diff --git a/build/edgeset.go b/build/edgeset.go index 03e49e3..fd8bc19 100644 --- a/build/edgeset.go +++ b/build/edgeset.go @@ -62,7 +62,7 @@ func (g *Virtual) Delete(e EdgeSet) *Virtual { }) } -// NewEdges returns a virtual graph with n vertices and all edges +// newEdges returns a virtual graph with n vertices and all edges // belonging to the edge set. func newEdges(n int, e EdgeSet) *Virtual { switch { diff --git a/build/join.go b/build/join.go index 2c23d6f..8a159d2 100644 --- a/build/join.go +++ b/build/join.go @@ -28,7 +28,6 @@ func (g1 *Virtual) Join(g2 *Virtual, bridge EdgeSet) *Virtual { default: return bridge.Cost(v, w) } - return bridge.Cost(v, w) } var noFilter bool diff --git a/build/vertexset.go b/build/vertexset.go index dcfdb27..49a6efd 100644 --- a/build/vertexset.go +++ b/build/vertexset.go @@ -22,7 +22,7 @@ type interval struct { index int // index of a in the whole set } -// updates the index values. +// update updates the index values. func (s VertexSet) update() { prev := 0 for i, in := range s.set { @@ -46,7 +46,7 @@ func Range(a, b int) VertexSet { // Vertex returns a set containing the single vertex v. func Vertex(v int) VertexSet { - return Range(v, v+1) + return VertexSet{[]interval{{v, v + 1, 0}}} } // size returns the number of elements in this set, or -1 for the universe. diff --git a/euler.go b/euler.go index dce3795..52f959a 100644 --- a/euler.go +++ b/euler.go @@ -4,15 +4,19 @@ package graph // If no such walk exists, it returns an empty walk and sets ok to false. func EulerDirected(g Iterator) (walk []int, ok bool) { n := g.Order() - // Compute outdegree - indegree for each vertex. - degree := make([]int, n) + degree := make([]int, n) // outdegree - indegree for each vertex + edgeCount := 0 for v := range degree { g.Visit(v, func(w int, _ int64) (skip bool) { + edgeCount++ degree[v]++ degree[w]-- return }) } + if edgeCount == 0 { + return []int{}, true + } start, end := -1, -1 for v := range degree { @@ -28,30 +32,28 @@ func EulerDirected(g Iterator) (walk []int, ok bool) { } // Make a copy of g - edgeCount := 0 h := make([][]int, n) for v := range h { g.Visit(v, func(w int, _ int64) (skip bool) { h[v] = append(h[v], w) - edgeCount++ return }) } - if edgeCount == 0 { - return []int{}, true - } // Find a starting point with neighbors. - for v := 0; v < n && start == -1; v++ { - if len(h[v]) > 0 { - start = v + if start == -1 { + for v, neighbors := range h { + if len(neighbors) > 0 { + start = v + break + } } } - stack := []int{start} - for len(stack) > 0 { - v := stack[len(stack)-1] - stack = stack[:len(stack)-1] + for stack := []int{start}; len(stack) > 0; { + n := len(stack) + v := stack[n-1] + stack = stack[:n-1] for len(h[v]) > 0 { stack = append(stack, v) v, h[v] = h[v][0], h[v][1:] @@ -59,11 +61,9 @@ func EulerDirected(g Iterator) (walk []int, ok bool) { } walk = append(walk, v) } - - if edgeCount != 0 { + if edgeCount > 0 { return []int{}, false } - for i, j := 0, len(walk)-1; i < j; i, j = i+1, j-1 { walk[i], walk[j] = walk[j], walk[i] } @@ -75,16 +75,20 @@ func EulerDirected(g Iterator) (walk []int, ok bool) { // and sets ok to false. func EulerUndirected(g Iterator) (walk []int, ok bool) { n := g.Order() - // Compute outdegree for each vertex. - out := make([]int, n) + out := make([]int, n) // outdegree for each vertex + edgeCount := 0 for v := range out { g.Visit(v, func(w int, _ int64) (skip bool) { + edgeCount++ if v != w { out[v]++ } return }) } + if edgeCount == 0 { + return []int{}, true + } start, oddDeg := -1, 0 for v := range out { @@ -97,32 +101,23 @@ func EulerUndirected(g Iterator) (walk []int, ok bool) { return []int{}, false } - // Make a copy of g. - edgeCount := 0 - h := New(n) - for v := 0; v < n; v++ { - g.Visit(v, func(w int, _ int64) (skip bool) { - h.Add(v, w) - edgeCount++ - return - }) - } - if edgeCount == 0 { - return []int{}, true - } - // Find a starting point with neighbors. - for v := 0; v < n && start == -1; v++ { - h.Visit(v, func(w int, _ int64) (skip bool) { - start = w - return true - }) + if start == -1 { + for v := 0; v < n; v++ { + if g.Visit(v, func(w int, _ int64) (skip bool) { + start = w + return true + }) { + break + } + } } - stack := []int{start} - for len(stack) > 0 { - v := stack[len(stack)-1] - stack = stack[:len(stack)-1] + h := Copy(g) + for stack := []int{start}; len(stack) > 0; { + n := len(stack) + v := stack[n-1] + stack = stack[:n-1] for h.Degree(v) > 0 { stack = append(stack, v) var w int @@ -130,19 +125,16 @@ func EulerUndirected(g Iterator) (walk []int, ok bool) { w = u return true }) + h.DeleteBoth(v, w) + edgeCount-- if v != w { - h.DeleteBoth(v, w) - edgeCount -= 2 - } else { - h.Delete(v, v) edgeCount-- } v = w } walk = append(walk, v) } - - if edgeCount != 0 { + if edgeCount > 0 { return []int{}, false } return walk, true diff --git a/example_dfs_test.go b/example_dfs_test.go index b265a18..2c83567 100644 --- a/example_dfs_test.go +++ b/example_dfs_test.go @@ -64,8 +64,7 @@ func (d *DFSData) dfsVisit(g graph.Iterator, v int) { d.Finish[v] = d.Time } -// An implementation of depth-first search -// demonstrating how to use this package. +// Show how to use this package by implementing a complete depth-first search. func Example_dFS() { // Build a small directed graph: // diff --git a/examples_test.go b/examples_test.go index de0d1a8..240de09 100644 --- a/examples_test.go +++ b/examples_test.go @@ -5,7 +5,7 @@ import ( "github.com/yourbasic/graph" ) -// How to iterate over the edges of a graph. +// Build a plain graph and visit all of its edges. func Example_basics() { // Build a graph with four vertices and four undirected edges. // (Each of these edges are, in fact, represented by two directed diff --git a/graph.go b/graph.go index 8668694..4454983 100644 --- a/graph.go +++ b/graph.go @@ -36,10 +36,12 @@ // by composing and filtering a set of standard graphs, or by writing // functions that describe the edges of a graph. // -// Example usage +// Tutorial // -// The package examples show how to build plain graphs and how to efficiently -// use the Visit iterator, the vital abstraction of this package. +// The Basics example shows how to build a plain graph and how to +// efficiently use the Visit iterator, the key abstraction of this package. +// +// The DFS example contains a full implementation of depth-first search. // package graph @@ -63,7 +65,7 @@ type Iterator interface { // • The calls to the do function may occur in any order, // and the order may vary. // - Visit(v int, Do func(w int, c int64) (skip bool)) (aborted bool) + Visit(v int, do func(w int, c int64) (skip bool)) (aborted bool) } // The maximum and minum value of an edge cost. diff --git a/immutable.go b/immutable.go index de3d50a..8f66979 100644 --- a/immutable.go +++ b/immutable.go @@ -24,6 +24,9 @@ type neighbor struct { // Sort returns an immutable copy of g with a Visit method // that returns its neighbors in increasing numerical order. func Sort(g Iterator) *Immutable { + if g, ok := g.(*Immutable); ok { + return g + } return build(g, false) } @@ -36,9 +39,6 @@ func Transpose(g Iterator) *Immutable { } func build(g Iterator, transpose bool) *Immutable { - if g, ok := g.(*Immutable); ok && !transpose { - return g - } n := g.Order() h := &Immutable{edges: make([][]neighbor, n)} for v := range h.edges { @@ -81,7 +81,6 @@ func build(g Iterator, transpose bool) *Immutable { prev = w } } - } return h } diff --git a/maxflow.go b/maxflow.go index ac55075..641db44 100644 --- a/maxflow.go +++ b/maxflow.go @@ -7,14 +7,8 @@ package graph func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { // Edmonds-Karp's algorithm n := g.Order() - residual := New(n) - for v := 0; v < n; v++ { - g.Visit(v, func(w int, c int64) (skip bool) { - residual.AddCost(v, w, c) - return - }) - } prev := make([]int, n) + residual := Copy(g) for residualFlow(residual, s, t, prev) && flow < Max { pathFlow := Max for v := t; v != s; { @@ -41,11 +35,10 @@ func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { return }) } - graph = Sort(res) - return + return flow, Sort(res) } -func residualFlow(g Iterator, s, t int, prev []int) bool { +func residualFlow(g *Mutable, s, t int, prev []int) bool { visited := make([]bool, g.Order()) prev[s], visited[s] = -1, true for queue := []int{s}; len(queue) > 0; { diff --git a/maxflow_test.go b/maxflow_test.go index 5278442..350d8a5 100644 --- a/maxflow_test.go +++ b/maxflow_test.go @@ -69,7 +69,6 @@ func TestMaxFlow(t *testing.T) { } } - func BenchmarkMaxFlow(b *testing.B) { n := 50 b.StopTimer() diff --git a/mutable.go b/mutable.go index 5477b1c..ad6cefa 100644 --- a/mutable.go +++ b/mutable.go @@ -46,12 +46,12 @@ func Copy(g Iterator) *Mutable { func copyMutable(g *Mutable) *Mutable { h := New(g.Order()) - for v := range g.edges { - if deg := len(g.edges[v]); deg > 0 { + for v, neighbors := range g.edges { + if deg := len(neighbors); deg > 0 { h.edges[v] = make(map[int]int64, deg) - } - for w, c := range g.edges[v] { - h.edges[v][w] = c + for w, c := range neighbors { + h.edges[v][w] = c + } } } return h @@ -59,12 +59,12 @@ func copyMutable(g *Mutable) *Mutable { func copyImmutable(g *Immutable) *Mutable { h := New(g.Order()) - for v := range g.edges { - if deg := len(g.edges[v]); deg > 0 { + for v, neighbors := range g.edges { + if deg := len(neighbors); deg > 0 { h.edges[v] = make(map[int]int64, deg) - } - for _, edge := range g.edges[v] { - h.edges[v][edge.vertex] = edge.cost + for _, edge := range neighbors { + h.edges[v][edge.vertex] = edge.cost + } } } return h