Skip to content
This repository has been archived by the owner on Apr 26, 2019. It is now read-only.

{search,traverse}: clean up after recent changes #44

Merged
merged 5 commits into from May 12, 2015
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions internal/set.go
@@ -0,0 +1,32 @@
// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package internal

// A set is a set of integer identifiers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A set -> IntSet

And capital letters in the comments below.

type IntSet map[int]struct{}

// The simple accessor methods for Set are provided to allow ease of
// implementation change should the need arise.

// add inserts an element into the set.
func (s IntSet) Add(e int) {
s[e] = struct{}{}
}

// has reports the existence of the element in the set.
func (s IntSet) Has(e int) bool {
_, ok := s[e]
return ok
}

// remove deletes the specified element from the set.
func (s IntSet) Remove(e int) {
delete(s, e)
}

// count reports the number of elements stored in the set.
func (s IntSet) Count() int {
return len(s)
}
28 changes: 28 additions & 0 deletions internal/sort.go
@@ -0,0 +1,28 @@
// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package internal

// BySliceValues implements the sort.Interface sorting a slice of
// []int lexically by the values of the []int.
type BySliceValues [][]int

func (c BySliceValues) Len() int { return len(c) }
func (c BySliceValues) Less(i, j int) bool {
a, b := c[i], c[j]
l := len(a)
if len(b) < l {
l = len(b)
}
for k, v := range a[:l] {
if v < b[k] {
return true
}
if v > b[k] {
return false
}
}
return len(a) < len(b)
}
func (c BySliceValues) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
31 changes: 16 additions & 15 deletions search/graph_search.go
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/gonum/graph"
"github.com/gonum/graph/concrete"
"github.com/gonum/graph/internal"
"github.com/gonum/graph/traverse"
)

Expand Down Expand Up @@ -288,25 +289,25 @@ func DepthFirstSearch(start, goal graph.Node, g graph.Graph) []graph.Node {
sf := setupFuncs(g, nil, nil)
successors := sf.successors

closedSet := make(intSet)
closedSet := make(internal.IntSet)
openSet := &nodeStack{start}
predecessor := make(map[int]graph.Node)

for openSet.len() != 0 {
curr := openSet.pop()

if closedSet.has(curr.ID()) {
if closedSet.Has(curr.ID()) {
continue
}

if curr.ID() == goal.ID() {
return rebuildPath(predecessor, goal)
}

closedSet.add(curr.ID())
closedSet.Add(curr.ID())

for _, neighbor := range successors(curr) {
if closedSet.has(neighbor.ID()) {
if closedSet.Has(neighbor.ID()) {
continue
}

Expand Down Expand Up @@ -386,7 +387,7 @@ func TarjanSCC(g graph.DirectedGraph) [][]graph.Node {

indexTable: make(map[int]int, len(nodes)),
lowLink: make(map[int]int, len(nodes)),
onStack: make(intSet, len(nodes)),
onStack: make(internal.IntSet, len(nodes)),
}
for _, v := range nodes {
if t.indexTable[v.ID()] == 0 {
Expand All @@ -407,7 +408,7 @@ type tarjan struct {
index int
indexTable map[int]int
lowLink map[int]int
onStack intSet
onStack internal.IntSet

stack []graph.Node

Expand All @@ -424,7 +425,7 @@ func (t *tarjan) strongconnect(v graph.Node) {
t.indexTable[vID] = t.index
t.lowLink[vID] = t.index
t.stack = append(t.stack, v)
t.onStack.add(vID)
t.onStack.Add(vID)

// Consider successors of v.
for _, w := range t.succ(v) {
Expand All @@ -433,7 +434,7 @@ func (t *tarjan) strongconnect(v graph.Node) {
// Successor w has not yet been visited; recur on it.
t.strongconnect(w)
t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID])
} else if t.onStack.has(wID) {
} else if t.onStack.Has(wID) {
// Successor w is in stack s and hence in the current SCC.
t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID])
}
Expand All @@ -448,7 +449,7 @@ func (t *tarjan) strongconnect(v graph.Node) {
)
for {
w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1]
t.onStack.remove(w.ID())
t.onStack.Remove(w.ID())
// Add w to current strongly connected component.
scc = append(scc, w)
if w.ID() == vID {
Expand Down Expand Up @@ -507,17 +508,17 @@ func Prim(dst graph.MutableGraph, g graph.EdgeListGraph, cost graph.CostFunc) {
}

dst.AddNode(nlist[0])
remainingNodes := make(intSet)
remainingNodes := make(internal.IntSet)
for _, node := range nlist[1:] {
remainingNodes.add(node.ID())
remainingNodes.Add(node.ID())
}

edgeList := g.EdgeList()
for remainingNodes.count() != 0 {
for remainingNodes.Count() != 0 {
var edges []concrete.WeightedEdge
for _, edge := range edgeList {
if (dst.NodeExists(edge.Head()) && remainingNodes.has(edge.Tail().ID())) ||
(dst.NodeExists(edge.Tail()) && remainingNodes.has(edge.Head().ID())) {
if (dst.NodeExists(edge.Head()) && remainingNodes.Has(edge.Tail().ID())) ||
(dst.NodeExists(edge.Tail()) && remainingNodes.Has(edge.Head().ID())) {

edges = append(edges, concrete.WeightedEdge{Edge: edge, Cost: cost(edge)})
}
Expand All @@ -527,7 +528,7 @@ func Prim(dst graph.MutableGraph, g graph.EdgeListGraph, cost graph.CostFunc) {
myEdge := edges[0]

dst.AddUndirectedEdge(myEdge.Edge, myEdge.Cost)
remainingNodes.remove(myEdge.Edge.Head().ID())
remainingNodes.Remove(myEdge.Edge.Head().ID())
}

}
Expand Down
60 changes: 26 additions & 34 deletions search/johnson_cycles.go
Expand Up @@ -8,6 +8,7 @@ import (
"sort"

"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)

// johnson implements Johnson's "Finding all the elementary
Expand All @@ -16,8 +17,8 @@ import (
// Comments in the johnson methods are kept in sync with the comments
// and labels from the paper.
type johnson struct {
adjacent johnsonGraph // SCC adjacency list.
b []intSet // Johnson's "B-list".
adjacent johnsonGraph // SCC adjacency list.
b []internal.IntSet // Johnson's "B-list".
blocked []bool
s int

Expand All @@ -31,7 +32,7 @@ func CyclesIn(g graph.DirectedGraph) [][]graph.Node {
jg := johnsonGraphFrom(g)
j := johnson{
adjacent: jg,
b: make([]intSet, len(jg.orig)),
b: make([]internal.IntSet, len(jg.orig)),
blocked: make([]bool, len(jg.orig)),
}

Expand All @@ -51,12 +52,12 @@ func CyclesIn(g graph.DirectedGraph) [][]graph.Node {
j.s = s
}
for i, v := range j.adjacent.orig {
if !j.adjacent.nodes.has(v.ID()) {
if !j.adjacent.nodes.Has(v.ID()) {
continue
}
if len(j.adjacent.succ[v.ID()]) > 0 {
j.blocked[i] = false
j.b[i] = make(intSet)
j.b[i] = make(internal.IntSet)
}
}
//L3:
Expand Down Expand Up @@ -96,7 +97,7 @@ func (j *johnson) circuit(v int) bool {
j.unblock(v)
} else {
for w := range j.adjacent.succ[n.ID()] {
j.b[j.adjacent.indexOf(w)].add(v)
j.b[j.adjacent.indexOf(w)].Add(v)
}
}
j.stack = j.stack[:len(j.stack)-1]
Expand All @@ -108,7 +109,7 @@ func (j *johnson) circuit(v int) bool {
func (j *johnson) unblock(u int) {
j.blocked[u] = false
for w := range j.b[u] {
j.b[u].remove(w)
j.b[u].Remove(w)
if j.blocked[w] {
j.unblock(w)
}
Expand All @@ -124,8 +125,8 @@ type johnsonGraph struct {
orig []graph.Node
index map[int]int

nodes intSet
succ map[int]intSet
nodes internal.IntSet
succ map[int]internal.IntSet
}

// johnsonGraphFrom returns a deep copy of the graph g.
Expand All @@ -136,18 +137,18 @@ func johnsonGraphFrom(g graph.DirectedGraph) johnsonGraph {
orig: nodes,
index: make(map[int]int, len(nodes)),

nodes: make(intSet, len(nodes)),
succ: make(map[int]intSet),
nodes: make(internal.IntSet, len(nodes)),
succ: make(map[int]internal.IntSet),
}
for i, u := range nodes {
c.index[u.ID()] = i
for _, v := range g.Successors(u) {
if c.succ[u.ID()] == nil {
c.succ[u.ID()] = make(intSet)
c.nodes.add(u.ID())
c.succ[u.ID()] = make(internal.IntSet)
c.nodes.Add(u.ID())
}
c.nodes.add(v.ID())
c.succ[u.ID()].add(v.ID())
c.nodes.Add(v.ID())
c.succ[u.ID()].Add(v.ID())
}
}
return c
Expand All @@ -160,7 +161,7 @@ func (n byID) Less(i, j int) bool { return n[i].ID() < n[j].ID() }
func (n byID) Swap(i, j int) { n[i], n[j] = n[j], n[i] }

// order returns the order of the graph.
func (g johnsonGraph) order() int { return g.nodes.count() }
func (g johnsonGraph) order() int { return g.nodes.Count() }

// indexOf returns the index of the retained node for the given node ID.
func (g johnsonGraph) indexOf(id int) int {
Expand All @@ -170,35 +171,26 @@ func (g johnsonGraph) indexOf(id int) int {
// leastVertexIndex returns the index into orig of the least vertex.
func (g johnsonGraph) leastVertexIndex() int {
for _, v := range g.orig {
if g.nodes.has(v.ID()) {
if g.nodes.Has(v.ID()) {
return g.indexOf(v.ID())
}
}
panic("johnsonCycles: empty set")
}

// remove deletes edges that make up the given paths from the graph.
func (g johnsonGraph) remove(paths [][]int) {
for _, p := range paths {
for i, u := range p[:len(p)-1] {
g.succ[u].remove(p[i+1])
}
}
}

// subgraph returns a subgraph of g induced by {s, s+1, ... , n}. The
// subgraph is destructively generated in g.
func (g johnsonGraph) subgraph(s int) johnsonGraph {
sn := g.orig[s].ID()
for u, e := range g.succ {
if u < sn {
g.nodes.remove(u)
g.nodes.Remove(u)
delete(g.succ, u)
continue
}
for v := range e {
if v < sn {
g.succ[u].remove(v)
g.succ[u].Remove(v)
}
}
}
Expand All @@ -218,8 +210,8 @@ func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
sub := johnsonGraph{
orig: g.orig,
index: g.index,
nodes: make(intSet),
succ: make(map[int]intSet),
nodes: make(internal.IntSet),
succ: make(map[int]internal.IntSet),
}

var n int
Expand All @@ -232,11 +224,11 @@ func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
for _, v := range scc {
if _, ok := g.succ[u.ID()][v.ID()]; ok {
if sub.succ[u.ID()] == nil {
sub.succ[u.ID()] = make(intSet)
sub.nodes.add(u.ID())
sub.succ[u.ID()] = make(internal.IntSet)
sub.nodes.Add(u.ID())
}
sub.nodes.add(v.ID())
sub.succ[u.ID()].add(v.ID())
sub.nodes.Add(v.ID())
sub.succ[u.ID()].Add(v.ID())
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions search/johnson_cycles_test.go
Expand Up @@ -10,6 +10,7 @@ import (
"testing"

"github.com/gonum/graph/concrete"
"github.com/gonum/graph/internal"
"github.com/gonum/graph/search"
)

Expand Down Expand Up @@ -77,8 +78,8 @@ var cyclesInTests = []struct {
4: linksTo(3),
},
want: [][]int{
{3, 4, 3},
{0, 1, 2, 0},
{3, 4, 3},
},
},
}
Expand Down Expand Up @@ -112,7 +113,7 @@ func TestCyclesIn(t *testing.T) {
}
got[j] = ids
}
sort.Sort(byComponentLengthOrStart(got))
sort.Sort(internal.BySliceValues(got))
if !reflect.DeepEqual(got, test.want) {
t.Errorf("unexpected johnson result for %d:\n\tgot:%#v\n\twant:%#v", i, got, test.want)
}
Expand Down