Skip to content

Commit b2439f1

Browse files
committed
TopologicalSort now supports more generic input, updated examples
1 parent fee9cf7 commit b2439f1

File tree

7 files changed

+152
-42
lines changed

7 files changed

+152
-42
lines changed

README.md

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,27 @@
11
# gorder
2-
Golang implementation of topological sorting algorithms for fast ordering.
2+
3+
A golang implementation of topological sorting algorithms for fast ordering.
4+
5+
## Installation:
6+
7+
`$ go get -u github.com/amwolff/gorder`
8+
9+
## Usage:
10+
11+
Check `cmd/example/example.go` for example usage.
12+
13+
```
14+
$ go run cmd/example/example.go
15+
Solution (Kahn): [1 {3.141592653589793} 3 5 4]
16+
Solution (DFS-based): [1 {3.141592653589793} 3 5 4]
17+
```
18+
19+
## Notes:
20+
21+
* Maps are one of the common ways to store graphs in Go. The `TopologicalSort` function input supports `map[interface{}][]interface{}` maps.
22+
23+
* Sub-package `dagenerator` was developed and used for generating random DAGs for performance tests (`cmd/measurements` should be rewritten as benchmarks).
24+
25+
* Implementation of Kahn's algorithm does pre-computation in the beginning of it's work in order to calculate indegree of every vertex in the graph. This may be split into two separate algorithms/functions in the future:
26+
- one that doesn't take any additional input but the graph (current implementation)
27+
- and one that takes additional input of indegrees

cmd/example-2/example_2.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/amwolff/gorder"
7+
"github.com/sirupsen/logrus"
8+
)
9+
10+
func serialize(m map[int][]int) map[interface{}][]interface{} {
11+
s := make(map[interface{}][]interface{})
12+
for k, v := range m {
13+
for _, val := range v {
14+
s[k] = append(s[k], val)
15+
}
16+
}
17+
return s
18+
}
19+
20+
func deserialize(o []interface{}) []int {
21+
d := make([]int, len(o))
22+
for i := range o {
23+
d[i] = o[i].(int)
24+
}
25+
return d
26+
}
27+
28+
func main() {
29+
var (
30+
output []interface{}
31+
err error
32+
)
33+
34+
digraph := map[int][]int{
35+
1: []int{2, 4},
36+
2: []int{3, 5},
37+
3: []int{4, 5},
38+
}
39+
40+
output, err = gorder.TopologicalSort(serialize(digraph), "kahn")
41+
if err != nil {
42+
logrus.Fatal(err)
43+
}
44+
fmt.Printf("Solution (Kahn): %d\n", deserialize(output))
45+
46+
output, err = gorder.TopologicalSort(serialize(digraph), "dfsbased")
47+
if err != nil {
48+
logrus.Fatal(err)
49+
}
50+
fmt.Printf("Solution (DFS-based): %d\n", deserialize(output))
51+
}

cmd/example/example.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"math"
6+
7+
"github.com/amwolff/gorder"
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
func main() {
12+
var (
13+
a struct{ Pi float64 }
14+
o []interface{}
15+
err error
16+
)
17+
18+
a.Pi = math.Pi
19+
20+
digraph := map[interface{}][]interface{}{
21+
1: []interface{}{a, "4"},
22+
a: []interface{}{3, "5"},
23+
3: []interface{}{"4", "5"},
24+
}
25+
26+
o, err = gorder.TopologicalSort(digraph, "kahn")
27+
if err != nil {
28+
logrus.Fatal(err)
29+
}
30+
fmt.Printf("Solution (Kahn): %v\n", o)
31+
32+
o, err = gorder.TopologicalSort(digraph, "dfsbased")
33+
if err != nil {
34+
logrus.Fatal(err)
35+
}
36+
fmt.Printf("Solution (DFS-based): %v\n", o)
37+
}

cmd/measurements/measurements.go

+5-8
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,23 @@ import (
1010
)
1111

1212
func main() {
13-
logger := logrus.StandardLogger()
14-
logger.SetLevel(logrus.DebugLevel)
15-
1613
start := time.Now()
1714
graph := dagenerator.Generate(10, 50, 30, 50, 30)
18-
logger.Infof("DAG generation time: %v", time.Since(start))
15+
logrus.Infof("DAG generation time: %v", time.Since(start))
1916

2017
start = time.Now()
2118
s, err := gorder.TopologicalSort(graph, "kahn")
22-
logger.Infof("Kahn resolve time: %v", time.Since(start))
19+
logrus.Infof("Kahn resolve time: %v", time.Since(start))
2320
if err != nil {
24-
logger.Fatal(err)
21+
logrus.Fatal(err)
2522
}
2623
spew.Dump(s)
2724

2825
start = time.Now()
2926
s, err = gorder.TopologicalSort(graph, "dfsbased")
30-
logger.Infof("DFS-based resolve time: %v", time.Since(start))
27+
logrus.Infof("DFS-based resolve time: %v", time.Since(start))
3128
if err != nil {
32-
logger.Fatal(err)
29+
logrus.Fatal(err)
3330
}
3431
spew.Dump(s)
3532
}

dagenerator/dagenerator.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
"time"
88
)
99

10-
func Generate(minPerRank, maxPerRank, minRanks, maxRanks, percent int) (digraph map[int][]int) {
10+
func Generate(minPerRank, maxPerRank, minRanks, maxRanks, percent int) (digraph map[interface{}][]interface{}) {
1111
var j, k, nodes int
1212
random := rand.New(rand.NewSource(time.Now().UnixNano()))
13-
digraph = make(map[int][]int)
13+
digraph = make(map[interface{}][]interface{})
1414

1515
ranks := minRanks + int(math.Mod(random.Float64(), float64(maxRanks-minRanks+1)))
1616
for i := 0; i < ranks; i++ {

gorder.go

+21-21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"regexp"
66
)
77

8-
func TopologicalSort(digraph map[int][]int, algorithm string) (solution []int, err error) {
8+
func TopologicalSort(digraph map[interface{}][]interface{}, algorithm string) (solution []interface{}, err error) {
99
kahnRgxp, err := regexp.Compile(`[Kk]ahn\z`)
1010
if err != nil {
1111
return nil, err
@@ -29,57 +29,57 @@ func TopologicalSort(digraph map[int][]int, algorithm string) (solution []int, e
2929
return solution, nil
3030
}
3131

32-
func kahn(digraph map[int][]int) ([]int, error) {
33-
inDegree := make(map[int]int)
32+
func kahn(digraph map[interface{}][]interface{}) ([]interface{}, error) {
33+
indegrees := make(map[interface{}]int)
3434
for u := range digraph {
3535
if digraph[u] != nil {
3636
for _, v := range digraph[u] {
37-
inDegree[v]++
37+
indegrees[v]++
3838
}
3939
}
4040
}
4141

42-
var queue []int
42+
var queue []interface{}
4343
for u := range digraph {
44-
if _, ok := inDegree[u]; !ok {
44+
if _, ok := indegrees[u]; !ok {
4545
queue = append(queue, u)
4646
}
4747
}
4848

49-
var order []int
49+
var order []interface{}
5050
for len(queue) > 0 {
5151
u := queue[len(queue)-1]
5252
queue = queue[:(len(queue) - 1)]
5353
order = append(order, u)
5454
for _, v := range digraph[u] {
55-
inDegree[v]--
56-
if inDegree[v] == 0 {
55+
indegrees[v]--
56+
if indegrees[v] == 0 {
5757
queue = append(queue, v)
5858
}
5959
}
6060
}
6161

62-
for _, in := range inDegree {
63-
if in > 0 {
64-
return order, errors.New("Kahn: not a DAG")
62+
for _, indegree := range indegrees {
63+
if indegree > 0 {
64+
return order, errors.New("not a DAG")
6565
}
6666
}
6767
return order, nil
6868
}
6969

70-
func dfsBased(digraph map[int][]int) ([]int, error) {
70+
func dfsBased(digraph map[interface{}][]interface{}) ([]interface{}, error) {
7171
var (
7272
acyclic = true
73-
order []int
74-
permanentMark = make(map[int]bool)
75-
temporaryMark = make(map[int]bool)
76-
visit func(int)
73+
order []interface{}
74+
permanentMark = make(map[interface{}]bool)
75+
temporaryMark = make(map[interface{}]bool)
76+
visit func(interface{})
7777
)
7878

79-
visit = func(u int) {
79+
visit = func(u interface{}) {
8080
if temporaryMark[u] {
8181
acyclic = false
82-
} else if !permanentMark[u] {
82+
} else if !(temporaryMark[u] || permanentMark[u]) {
8383
temporaryMark[u] = true
8484
for _, v := range digraph[u] {
8585
visit(v)
@@ -89,15 +89,15 @@ func dfsBased(digraph map[int][]int) ([]int, error) {
8989
}
9090
delete(temporaryMark, u)
9191
permanentMark[u] = true
92-
order = append([]int{u}, order...)
92+
order = append([]interface{}{u}, order...)
9393
}
9494
}
9595

9696
for u := range digraph {
9797
if !permanentMark[u] {
9898
visit(u)
9999
if !acyclic {
100-
return order, errors.New("DFS-based: not a DAG")
100+
return order, errors.New("not a DAG")
101101
}
102102
}
103103
}

gorder_test.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package gorder
33
import "testing"
44

55
func TestTopologicalSort(t *testing.T) {
6-
digraph := map[int][]int{
7-
1: []int{2, 4},
8-
2: []int{3, 5},
9-
3: []int{4, 5},
6+
digraph := map[interface{}][]interface{}{
7+
1: []interface{}{2, 4},
8+
2: []interface{}{3, 5},
9+
3: []interface{}{4, 5},
1010
}
11+
1112
want := []int{1, 2, 3, 5, 4}
1213

1314
_, err := TopologicalSort(digraph, "ultrasuperfast")
@@ -35,17 +36,16 @@ func TestTopologicalSort(t *testing.T) {
3536
}
3637
}
3738

38-
graphWithCycles := map[int][]int{
39-
1: []int{2, 4},
40-
2: []int{3, 5},
41-
3: []int{4, 5},
42-
4: []int{2},
39+
graphWithCycles := map[interface{}][]interface{}{
40+
1: []interface{}{2, 4},
41+
2: []interface{}{3, 5},
42+
3: []interface{}{4, 5},
43+
4: []interface{}{2},
4344
}
4445
_, err = TopologicalSort(graphWithCycles, "kahn")
4546
if err == nil {
4647
t.Fatal("Kahn: should have returned an error")
4748
}
48-
4949
_, err = TopologicalSort(graphWithCycles, "dfsbased")
5050
if err == nil {
5151
t.Fatal("DFS-based: should have returned an error")

0 commit comments

Comments
 (0)