├── .gitignore ├── LICENSE.md ├── README.md ├── bfs.go ├── cmd ├── lg2raw │ ├── lg2raw │ └── lg2raw.go └── rawtest │ ├── main.go │ └── rawtest ├── consts.go ├── converter ├── cmd │ ├── edgelist2graph │ │ ├── edgelist2graph │ │ └── main.go │ └── lg2graph │ │ ├── lg2graph │ │ └── lgformat.go ├── edgelist.go └── lgformat.go ├── dijkstra.go ├── interface.go ├── parallelbfs.go ├── pardijkstra.go ├── persistence.go ├── simpleedge.go ├── simpleedgeiter.go ├── simplegraph.go ├── simplevertexiter.go ├── smallgraphs.go └── test ├── .gitignore ├── processuints.go ├── testedgelist.go ├── testgg.go ├── testmut.go ├── testptr.go └── testsortpairs.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.sg 3 | *.txt 4 | *.csv 5 | *.cpp 6 | *.lg 7 | *.raw 8 | .vscode 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021, Seth Bromberger All rights reserved. 2 | 3 | Please note the following limitation with respect to redistribution: 4 | 5 | **Redistribution in any form with modification is expressly prohibited.** 6 | 7 | Redistribution and use in source and binary forms without modification, and use in source and binary forms with modification, is permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | 1. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | 1. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gographs 2 | 3 | Graphs in Go. Optimized BFS and parallel BFS. 4 | 5 | Currently very much a work in progress. 6 | -------------------------------------------------------------------------------- /bfs.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/sbromberger/bitvec" 7 | "github.com/shawnsmithdev/zermelo/zuint32" 8 | ) 9 | 10 | const unvisited = math.MaxUint32 11 | 12 | // BFS computes a vector of levels from src and returns a vector 13 | // of vertices visited in order along with a vector of distances 14 | // indexed by vertex. 15 | func BFS(g Graph, src uint32) (vertexList, vertLevel []uint32) { 16 | nv := g.NumVertices() 17 | vertLevel = make([]uint32, nv) 18 | for i := uint32(0); i < nv; i++ { 19 | vertLevel[i] = unvisited 20 | } 21 | 22 | visited := bitvec.NewBitVec(nv) 23 | curLevel := make([]uint32, 0, nv) 24 | nextLevel := make([]uint32, 0, nv) 25 | nLevel := uint32(1) 26 | vertLevel[src] = 0 27 | visited.TrySet(src) 28 | curLevel = append(curLevel, src) 29 | vertexList = make([]uint32, 0, nv) 30 | vertexList = append(vertexList, src) 31 | for len(curLevel) > 0 { 32 | for _, v := range curLevel { 33 | for _, neighbor := range g.OutNeighbors(v) { 34 | if visited.TrySet(neighbor) { 35 | nextLevel = append(nextLevel, neighbor) 36 | vertLevel[neighbor] = nLevel 37 | vertexList = append(vertexList, neighbor) 38 | } 39 | } 40 | } 41 | nLevel++ 42 | curLevel = curLevel[:0] 43 | curLevel, nextLevel = nextLevel, curLevel 44 | zuint32.SortBYOB(curLevel, nextLevel[:nv]) 45 | } 46 | return vertexList, vertLevel 47 | } 48 | -------------------------------------------------------------------------------- /cmd/lg2raw/lg2raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbromberger/gographs/85b470c4fe3b8105697e40b114d31e2700031f14/cmd/lg2raw/lg2raw -------------------------------------------------------------------------------- /cmd/lg2raw/lg2raw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/sbromberger/graph/persistence" 8 | ) 9 | 10 | func main() { 11 | fn1 := os.Args[1] 12 | fn2 := os.Args[2] 13 | g, err := persistence.GraphFromLG(fn1) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | if err := persistence.SaveRaw(fn2, g); err != nil { 18 | log.Fatal(err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cmd/rawtest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/sbromberger/graph" 10 | "github.com/sbromberger/graph/persistence" 11 | ) 12 | 13 | func main() { 14 | g, err := persistence.ReadRaw(os.Args[1]) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | fmt.Println(g) 19 | 20 | fmt.Println("outneighbors(0) = ", g.OutNeighbors(0)) 21 | start := time.Now() 22 | v, w := graph.BFS(g, 0) 23 | t := time.Now() 24 | elapsed := t.Sub(start) 25 | fmt.Println("elapsed BFS = ", elapsed, ", len(v) = ", len(v), ", len(w) = ", len(w)) 26 | fmt.Println(v[:10]) 27 | fmt.Println(w[:10]) 28 | s := int(0) 29 | for _, n := range w { 30 | s += int(n) 31 | } 32 | fmt.Println(s) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/rawtest/rawtest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbromberger/gographs/85b470c4fe3b8105697e40b114d31e2700031f14/cmd/rawtest/rawtest -------------------------------------------------------------------------------- /consts.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "math" 4 | 5 | const ( 6 | u0 = uint32(0) 7 | maxDist = float32(math.MaxFloat32 - 1) 8 | ) 9 | -------------------------------------------------------------------------------- /converter/cmd/edgelist2graph/edgelist2graph: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbromberger/gographs/85b470c4fe3b8105697e40b114d31e2700031f14/converter/cmd/edgelist2graph/edgelist2graph -------------------------------------------------------------------------------- /converter/cmd/edgelist2graph/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/sbromberger/gographs/converter" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) != 2 { 13 | fmt.Println("Usage: ", os.Args[0], " infile outfile") 14 | os.Exit(1) 15 | } 16 | in := os.Args[1] 17 | out := os.Args[2] 18 | 19 | g, err := converter.ReadEdgeList(in) 20 | if err != nil { 21 | log.Fatalf("Can't read %s: %v", in, err) 22 | } 23 | err = g.Save(out) 24 | if err != nil { 25 | log.Fatalf("Can't write %s: %v", out, err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /converter/cmd/lg2graph/lg2graph: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbromberger/gographs/85b470c4fe3b8105697e40b114d31e2700031f14/converter/cmd/lg2graph/lg2graph -------------------------------------------------------------------------------- /converter/cmd/lg2graph/lgformat.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/sbromberger/gographs/converter" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) != 2 { 13 | fmt.Println("Usage: ", os.Args[0], " infile outfile") 14 | os.Exit(1) 15 | } 16 | in := os.Args[1] 17 | out := os.Args[2] 18 | 19 | g, err := converter.GraphFromLG(in) 20 | if err != nil { 21 | log.Fatalf("Can't read %s: %v", in, err) 22 | } 23 | err = g.Save(out) 24 | if err != nil { 25 | log.Fatalf("Can't write %s: %v", out, err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /converter/edgelist.go: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | 11 | graph "github.com/sbromberger/gographs" 12 | ) 13 | 14 | func splitLine(line string) []string { 15 | s := regexp.MustCompile(`[\,\s]+`).Split(line, -1) 16 | return s 17 | } 18 | 19 | func readEdgeList(scanner *bufio.Scanner, offset uint32) ([]uint32, []uint32, error) { 20 | var l string 21 | ss := make([]uint32, 0, 100) 22 | ds := make([]uint32, 0, 100) 23 | for scanner.Scan() { 24 | l = scanner.Text() 25 | 26 | if strings.HasPrefix(l, "#") { 27 | continue 28 | } 29 | pieces := splitLine(l) 30 | if len(pieces) != 2 { 31 | return []uint32{}, []uint32{}, fmt.Errorf("Parsing error: got %s", l) 32 | } 33 | u64, err := strconv.ParseUint(pieces[0], 10, 32) 34 | if err != nil { 35 | return []uint32{}, []uint32{}, fmt.Errorf("Parsing error: got %s", l) 36 | } 37 | v64, err := strconv.ParseUint(pieces[1], 10, 32) 38 | if err != nil { 39 | return []uint32{}, []uint32{}, fmt.Errorf("Parsing error: got %s", l) 40 | } 41 | u := uint32(u64) - offset 42 | v := uint32(v64) - offset 43 | ss = append(ss, u) 44 | ds = append(ds, v) 45 | } 46 | if err := scanner.Err(); err != nil { 47 | return []uint32{}, []uint32{}, fmt.Errorf("Other error: %v", err) 48 | } 49 | return ss, ds, nil 50 | } 51 | 52 | // ReadEdgeList returns a graph from an edgelist. 53 | func ReadEdgeList(fn string) (graph.SimpleGraph, error) { 54 | f, err := os.OpenFile(fn, os.O_RDONLY, 0644) 55 | if err != nil { 56 | return graph.SimpleGraph{}, err 57 | } 58 | defer f.Close() 59 | 60 | scanner := bufio.NewScanner(f) 61 | ss, ds, err := readEdgeList(scanner, 0) 62 | if err != nil { 63 | return graph.SimpleGraph{}, err 64 | } 65 | 66 | return graph.New(ss, ds) 67 | } 68 | -------------------------------------------------------------------------------- /converter/lgformat.go: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | 8 | graph "github.com/sbromberger/gographs" 9 | ) 10 | 11 | // GraphFromLG reads a graph from lightgraphs format. Ignores header. 12 | func GraphFromLG(fn string) (graph.SimpleGraph, error) { 13 | f, err := os.OpenFile(fn, os.O_RDONLY, 0644) 14 | if err != nil { 15 | return graph.SimpleGraph{}, fmt.Errorf("Open failed: %v", err) 16 | } 17 | defer f.Close() 18 | 19 | scanner := bufio.NewScanner(f) 20 | scanner.Scan() // read header 21 | 22 | ss, ds, err := readEdgeList(scanner, 1) // offset is 1 because Julia is 1-indexed 23 | if err != nil { 24 | return graph.SimpleGraph{}, err 25 | } 26 | return graph.New(ss, ds) 27 | } 28 | -------------------------------------------------------------------------------- /dijkstra.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/shawnsmithdev/zermelo/zuint32" 7 | ) 8 | 9 | // DijkstraState is a state holding Dijkstra Shortest Path info. 10 | type DijkstraState struct { 11 | Parents []uint32 12 | Dists []float32 13 | Pathcounts []uint32 14 | Predecessors [][]uint32 15 | } 16 | 17 | func (d DijkstraState) String() string { 18 | s := fmt.Sprintln("DijkstraState with") 19 | s += fmt.Sprintln(" Parents: ", d.Parents) 20 | s += fmt.Sprintln(" Dists: ", d.Dists) 21 | s += fmt.Sprintln(" PathCounts: ", d.Pathcounts) 22 | s += fmt.Sprintln(" Predecessors: ", d.Predecessors) 23 | return s 24 | } 25 | 26 | func min(a, b float32) float32 { 27 | if a < b { 28 | return a 29 | } 30 | return b 31 | } 32 | 33 | // Dijkstra performs a Dijkstra Shortest Paths calculation from vertex `src` and returns a DijkstraState. 34 | // If `withpreds` is true, track all predecessors. 35 | func Dijkstra(g Graph, src uint32, weightFn func(uint32, uint32) float32, withPreds bool) DijkstraState { 36 | nv := g.NumVertices() 37 | vertLevel := make([]uint32, nv) 38 | for i := u0; i < nv; i++ { 39 | vertLevel[i] = unvisited 40 | } 41 | curLevel := make([]uint32, 0, nv) 42 | nextLevel := make([]uint32, 0, nv) 43 | nLevel := uint32(2) 44 | parents := make([]uint32, nv) 45 | pathcounts := make([]uint32, nv) 46 | dists := make([]float32, nv) 47 | 48 | preds := make([][]uint32, 0) 49 | if withPreds { 50 | preds = make([][]uint32, nv) 51 | } 52 | 53 | for i := range dists { 54 | dists[i] = maxDist 55 | } 56 | 57 | vertLevel[src] = 0 58 | dists[src] = 0 59 | parents[src] = src 60 | pathcounts[src] = 1 61 | curLevel = append(curLevel, src) 62 | for len(curLevel) > 0 { 63 | for _, u := range curLevel { 64 | for _, v := range g.OutNeighbors(u) { 65 | alt := min(maxDist, dists[u]+weightFn(u, v)) 66 | if vertLevel[v] == unvisited { // if not visited 67 | dists[v] = alt 68 | parents[v] = u 69 | pathcounts[v] += pathcounts[u] 70 | if withPreds { 71 | preds[v] = append(preds[v], u) 72 | } 73 | nextLevel = append(nextLevel, v) 74 | vertLevel[v] = nLevel 75 | } else { 76 | if alt < dists[v] { 77 | dists[v] = alt 78 | parents[v] = u 79 | pathcounts[v] = 0 80 | if withPreds { 81 | preds[v] = preds[v][:0] 82 | } 83 | } 84 | if alt == dists[v] { 85 | pathcounts[v] += pathcounts[u] 86 | if withPreds { 87 | preds[v] = append(preds[v], u) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | fmt.Printf("completed level %d, size = %d\n", nLevel-1, len(nextLevel)) 94 | nLevel++ 95 | curLevel = curLevel[:0] 96 | curLevel, nextLevel = nextLevel, curLevel 97 | zuint32.SortBYOB(curLevel, nextLevel[:nv]) 98 | } 99 | pathcounts[src] = 1 100 | parents[src] = 0 101 | if withPreds { 102 | preds[src] = preds[src][:0] 103 | } 104 | ds := DijkstraState{ 105 | Parents: parents, 106 | Dists: dists, 107 | Pathcounts: pathcounts, 108 | Predecessors: preds, 109 | } 110 | return ds 111 | } 112 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file contains interface definitions for Graph and Edge types. 4 | 5 | type Edge interface { 6 | // Src returns the source index of the edge 7 | Src() uint32 8 | // Dst returns the destination index of the edge 9 | Dst() uint32 10 | } 11 | 12 | // EdgeList is a slice of edges 13 | type EdgeList []Edge 14 | 15 | // Len returns the length of an edgelist. 16 | func (e EdgeList) Len() int { 17 | return len(e) 18 | } 19 | 20 | func (e EdgeList) Swap(i, j int) { 21 | e[i], e[j] = e[j], e[i] 22 | } 23 | 24 | func (e EdgeList) Less(i, j int) bool { 25 | if e[i].Src() < e[j].Src() { 26 | return true 27 | } 28 | if e[i].Src() > e[j].Src() { 29 | return false 30 | } 31 | return e[i].Dst() < e[j].Dst() 32 | } 33 | 34 | type EdgeIter interface { 35 | Next() Edge 36 | Done() bool 37 | } 38 | 39 | type VertexIter interface { 40 | Next() uint32 41 | Done() bool 42 | } 43 | 44 | type Graph interface { 45 | // OutDegree returns the outdegree of vertex u. 46 | OutDegree(u uint32) uint32 47 | // InDegree returns the indegree of vertex u. 48 | InDegree(u uint32) uint32 49 | // OutNeighbors returns the out neighbors of vertex u. 50 | OutNeighbors(u uint32) []uint32 51 | // InNeighbors returns the in neighbors of vertex u. 52 | InNeighbors(u uint32) []uint32 53 | // HasEdge returns true if an edge exists between u and v. 54 | HasEdge(u, v uint32) bool 55 | // IsDirected is true if the graph is directed 56 | IsDirected() bool 57 | // NumEdges returns the number of edges in a graph. 58 | NumEdges() uint64 59 | // NumVertices returns the number of vertices in a graph. 60 | NumVertices() uint32 61 | // Edges returns an iterator to graph edges. 62 | Edges() EdgeIter 63 | Vertices() VertexIter 64 | } 65 | -------------------------------------------------------------------------------- /parallelbfs.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "runtime" 5 | "sync/atomic" 6 | 7 | "github.com/egonelbre/async" 8 | "github.com/sbromberger/bitvec" 9 | "github.com/shawnsmithdev/zermelo/zuint32" 10 | ) 11 | 12 | const ( 13 | // ReadBlockSize : the number of neighbors to process per block 14 | ReadBlockSize = 256 15 | // WriteBlockSize : number of empty cells to allocate initially for nextLevel 16 | WriteBlockSize = 256 17 | // MaxBlockSize : max(ReadBlockSize, WriteBlockSize) 18 | MaxBlockSize = 256 19 | // EmptySentinel : all ones. 20 | EmptySentinel = ^u0 21 | ) 22 | 23 | // Frontier is 24 | type Frontier struct { 25 | Data []uint32 // the vector of data 26 | Head uint32 // the index to the next unused element in the vector 27 | } 28 | 29 | // NextRead returns the low and high offsets into Frontier for reading ReadBlockSize blocks. 30 | // It increases Head by the ReadBlockSize. 31 | // Note: we only read from currLevel. 32 | func (front *Frontier) NextRead() (low, high uint32) { 33 | high = atomic.AddUint32(&front.Head, ReadBlockSize) 34 | low = high - ReadBlockSize 35 | if high > uint32(len(front.Data)) { 36 | high = uint32(len(front.Data)) 37 | } 38 | return 39 | } 40 | 41 | // NextWrite returns the low and high offsets into Frontier for writing WriteBlockSize blocks. 42 | // It increases Head by WriteBlockSize. 43 | // Note: we only write to nextLevel. 44 | func (front *Frontier) NextWrite() (low, high uint32) { 45 | high = atomic.AddUint32(&front.Head, WriteBlockSize) 46 | low = high - WriteBlockSize 47 | return 48 | } 49 | 50 | // Write inserts `v` into the next available position in the Frontier, allocating as necessary. 51 | // Note: we only write to nextLevel. 52 | func (front *Frontier) Write(low, high *uint32, v uint32) { 53 | if *low >= *high { 54 | *low, *high = front.NextWrite() 55 | } 56 | front.Data[*low] = v 57 | *low++ 58 | } 59 | 60 | // processLevel uses Frontiers to dequeue work from currLevel in ReadBlockSize increments. 61 | func processLevel(g Graph, currLevel, nextLevel *Frontier, visited *bitvec.ABitVec) { 62 | writeLow, writeHigh := u0, u0 63 | for { 64 | readLow, readHigh := currLevel.NextRead() // if currLevel still has vertices to process, get the indices of a ReadBlockSize block of them 65 | if readLow >= readHigh { // otherwise exit 66 | break 67 | } 68 | 69 | for _, v := range currLevel.Data[readLow:readHigh] { // get and loop through a slice of ReadBlockSize vertices from currLevel 70 | if v == EmptySentinel { // if we hit a sentinel within the block, skip it 71 | continue 72 | } 73 | 74 | neighbors := g.OutNeighbors(v) // get the outNeighbors of the vertex under inspection 75 | i := 0 76 | for ; i < len(neighbors)-3; i += 4 { // unroll loop for visited 77 | n1, n2, n3, n4 := neighbors[i], neighbors[i+1], neighbors[i+2], neighbors[i+3] 78 | x1, x2, x3, x4 := visited.GetBuckets4(n1, n2, n3, n4) 79 | if visited.TrySetWith(x1, n1) { // if not visited, add to the list of vertices for nextLevel 80 | nextLevel.Write(&writeLow, &writeHigh, n1) 81 | } 82 | if visited.TrySetWith(x2, n2) { 83 | nextLevel.Write(&writeLow, &writeHigh, n2) 84 | } 85 | if visited.TrySetWith(x3, n3) { 86 | nextLevel.Write(&writeLow, &writeHigh, n3) 87 | } 88 | if visited.TrySetWith(x4, n4) { 89 | nextLevel.Write(&writeLow, &writeHigh, n4) 90 | } 91 | } 92 | for _, n := range neighbors[i:] { // process any remaining (< 4) neighbors for this vertex 93 | if visited.TrySet(n) { 94 | nextLevel.Write(&writeLow, &writeHigh, n) 95 | } 96 | } 97 | } 98 | } 99 | 100 | for i := writeLow; i < writeHigh; i++ { 101 | nextLevel.Data[i] = EmptySentinel // ensure the rest of the nextLevel block is "empty" using the sentinel 102 | } 103 | } 104 | 105 | // ParallelBFS computes a vector of levels from src in parallel. 106 | func ParallelBFS(g Graph, src uint32, procs int) { 107 | N := g.NumVertices() 108 | vertLevel := make([]uint32, N) 109 | visited := bitvec.NewABitVec(N) 110 | 111 | maxSize := N + (MaxBlockSize * uint32(procs)) 112 | currLevel := &Frontier{make([]uint32, 0, maxSize), 0} 113 | nextLevel := &Frontier{make([]uint32, maxSize), 0} 114 | 115 | currentLevel := uint32(2) 116 | vertLevel[src] = 0 117 | visited.TrySet(src) 118 | 119 | currLevel.Data = append(currLevel.Data, src) 120 | 121 | wait := make(chan struct{}) 122 | for len(currLevel.Data) > 0 { // while we have vertices in currentLevel 123 | 124 | async.Spawn(procs, func(i int) { // spawn `procs` goroutines to process vertices in this level, 125 | runtime.LockOSThread() // using currLevel as the work queue. Make sure only one goroutine per thread. 126 | processLevel(g, currLevel, nextLevel, &visited) 127 | }, func() { wait <- struct{}{} }) 128 | 129 | <-wait // this is equivalent to using a WaitGroup but uses a single channel message instead. 130 | 131 | nextLevel.Data = nextLevel.Data[:nextLevel.Head] // "truncate" nextLevel.Data to just the valid data... 132 | // ... we need to do this because Frontier.ReadNext uses `len`. 133 | 134 | sentinelCount := u0 135 | // now sort nextLevel by block. After this, all data within a given block will be sorted. This ensures that 136 | // "most" data are ordered, which preserves some linearity in cache access, but this might not be significant. 137 | // More testing is needed. 138 | async.BlockIter(int(nextLevel.Head), procs, func(low, high int) { 139 | zuint32.SortBYOB(nextLevel.Data[low:high], currLevel.Data[low:high]) 140 | for index, v := range nextLevel.Data[low:high] { 141 | if v == EmptySentinel { 142 | atomic.AddUint32(&sentinelCount, uint32(high-low-index)) 143 | break 144 | } 145 | vertLevel[v] = currentLevel 146 | } 147 | }) 148 | 149 | // fmt.Printf("completed level %d, size = %d\n", currentLevel-1, len(nextLevel.Data)-int(sentinelCount)) 150 | 151 | currentLevel++ 152 | currLevel, nextLevel = nextLevel, currLevel 153 | currLevel.Head = 0 // start reading from 0 154 | // reset buffer for next level 155 | nextLevel.Data = nextLevel.Data[:maxSize:maxSize] // resize the buffer to `maxSize` elements. We don't care what's in it, because... 156 | nextLevel.Head = 0 // ... we start writing to index 0. 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /pardijkstra.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync/atomic" 7 | 8 | "github.com/egonelbre/async" 9 | "github.com/sbromberger/bitvec" 10 | "github.com/shawnsmithdev/zermelo/zuint32" 11 | ) 12 | 13 | // processDijkstraLevel uses Frontiers to dequeue work from currLevel in ReadBlockSize increments. 14 | func processDijkstraLevel(g Graph, currLevel, nextLevel *Frontier, visited *bitvec.ABitVec, dists []float32, parents []uint32, pathcounts []uint32) { 15 | writeLow, writeHigh := u0, u0 16 | for { 17 | readLow, readHigh := currLevel.NextRead() // if currLevel still has vertices to process, get the indices of a ReadBlockSize block of them 18 | if readLow >= readHigh { // otherwise exit 19 | break 20 | } 21 | 22 | for _, u := range currLevel.Data[readLow:readHigh] { // get and loop through a slice of ReadBlockSize vertices from currLevel 23 | if u == EmptySentinel { // if we hit a sentinel within the block, skip it 24 | continue 25 | } 26 | alt := min(maxDist, dists[u]+1) 27 | vs := g.OutNeighbors(u) // get the outNeighbors of the vertex under inspection 28 | // i := 0 29 | for _, v := range vs { 30 | // for ; i < len(vs)-3; i += 4 { // unroll loop for visited 31 | // v1, v2, v3, v4 := vs[i], vs[i+1], vs[i+2], vs[i+3] 32 | // x1, x2, x3, x4 := visited.GetBuckets4(v1, v2, v3, v4) 33 | // if visited.TrySetWith(x1, v1) { // if not visited, add to the list of vertices for nextLevel 34 | // nextLevel.Write(&writeLow, &writeHigh, v1) 35 | // dists[v1] = alt 36 | // parents[v1] = u 37 | // pathcounts[v1] += pathcounts[u] 38 | // } else { 39 | // if alt < dists[v1] { 40 | // dists[v1] = alt 41 | // parents[v1] = u 42 | // pathcounts[v1] = 0 43 | // } 44 | // if alt == dists[v1] { 45 | // pathcounts[v1] += pathcounts[u] 46 | // } 47 | // } 48 | // if visited.TrySetWith(x2, v2) { 49 | // nextLevel.Write(&writeLow, &writeHigh, v2) 50 | // dists[v2] = alt 51 | // dists[v2] = alt 52 | // parents[v2] = u 53 | // pathcounts[v2] += pathcounts[u] 54 | // } else { 55 | // if alt < dists[v2] { 56 | // dists[v2] = alt 57 | // parents[v2] = u 58 | // pathcounts[v2] = 0 59 | // } 60 | // if alt == dists[v2] { 61 | // pathcounts[v2] += pathcounts[u] 62 | // } 63 | // } 64 | // if visited.TrySetWith(x3, v3) { 65 | // nextLevel.Write(&writeLow, &writeHigh, v3) 66 | // dists[v3] = alt 67 | // dists[v3] = alt 68 | // parents[v3] = u 69 | // pathcounts[v3] += pathcounts[u] 70 | // } else { 71 | // if alt < dists[v3] { 72 | // dists[v3] = alt 73 | // parents[v3] = u 74 | // pathcounts[v3] = 0 75 | // } 76 | // if alt == dists[v3] { 77 | // pathcounts[v3] += pathcounts[u] 78 | // } 79 | // } 80 | // if visited.TrySetWith(x4, v4) { 81 | // nextLevel.Write(&writeLow, &writeHigh, v4) 82 | // dists[v4] = alt 83 | // dists[v4] = alt 84 | // parents[v4] = u 85 | // pathcounts[v4] += pathcounts[u] 86 | // } else { 87 | // if alt < dists[v4] { 88 | // dists[v4] = alt 89 | // parents[v4] = u 90 | // pathcounts[v4] = 0 91 | // } 92 | // if alt == dists[v4] { 93 | // pathcounts[v4] += pathcounts[u] 94 | // } 95 | // } 96 | // } 97 | // for _, v := range vs[i:] { // process any remaining (< 4) neighbors for this vertex 98 | if visited.TrySet(v) { 99 | nextLevel.Write(&writeLow, &writeHigh, v) 100 | dists[v] = alt 101 | parents[v] = u 102 | atomic.AddUint32(&pathcounts[v], pathcounts[u]) 103 | } else { 104 | if alt < dists[v] { 105 | dists[v] = alt 106 | parents[v] = u 107 | pathcounts[v] = 0 108 | } 109 | if alt == dists[v] { 110 | atomic.AddUint32(&pathcounts[v], pathcounts[u]) 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | for i := writeLow; i < writeHigh; i++ { 118 | nextLevel.Data[i] = EmptySentinel // ensure the rest of the nextLevel block is "empty" using the sentinel 119 | } 120 | } 121 | 122 | // ParallelDijkstra computes a vector of levels from src in parallel. 123 | func ParallelDijkstra(g Graph, src uint32, procs int) DijkstraState { 124 | N := g.NumVertices() 125 | vertLevel := make([]uint32, N) 126 | visited := bitvec.NewABitVec(N) 127 | 128 | maxSize := N + MaxBlockSize*uint32(procs) 129 | currLevel := &Frontier{make([]uint32, 0, maxSize), 0} 130 | nextLevel := &Frontier{make([]uint32, maxSize), 0} 131 | 132 | currentLevel := uint32(2) 133 | parents := make([]uint32, N) 134 | pathcounts := make([]uint32, N) 135 | dists := make([]float32, N) 136 | 137 | for i := range dists { 138 | dists[i] = maxDist 139 | } 140 | 141 | vertLevel[src] = 0 142 | dists[src] = 0 143 | parents[src] = src 144 | pathcounts[src] = 1 145 | visited.TrySet(src) 146 | 147 | currLevel.Data = append(currLevel.Data, src) 148 | 149 | wait := make(chan struct{}) 150 | for len(currLevel.Data) > 0 { // while we have vertices in currentLevel 151 | 152 | async.Spawn(procs, func(i int) { // spawn `procs` goroutines to process vertices in this level, 153 | runtime.LockOSThread() // using currLevel as the work queue. Make sure only one goroutine per thread. 154 | processDijkstraLevel(g, currLevel, nextLevel, &visited, dists, parents, pathcounts) 155 | }, func() { wait <- struct{}{} }) 156 | 157 | <-wait // this is equivalent to using a WaitGroup but uses a single channel message instead. 158 | 159 | nextLevel.Data = nextLevel.Data[:nextLevel.Head] // "truncate" nextLevel.Data to just the valid data... 160 | // ... we need to do this because Frontier.ReadNext uses `len`. 161 | 162 | sentinelCount := u0 163 | // now sort nextLevel by block. After this, all data within a given block will be sorted. This ensures that 164 | // "most" data are ordered, which preserves some linearity in cache access, but this might not be significant. 165 | // More testing is needed. 166 | async.BlockIter(int(nextLevel.Head), procs, func(low, high int) { 167 | zuint32.SortBYOB(nextLevel.Data[low:high], currLevel.Data[low:high]) 168 | for index, v := range nextLevel.Data[low:high] { 169 | if v == EmptySentinel { 170 | atomic.AddUint32(&sentinelCount, uint32(high-low-index)) 171 | break 172 | } 173 | vertLevel[v] = currentLevel 174 | } 175 | }) 176 | 177 | fmt.Printf("completed level %d, size = %d\n", currentLevel-1, len(nextLevel.Data)-int(sentinelCount)) 178 | 179 | currentLevel++ 180 | currLevel, nextLevel = nextLevel, currLevel 181 | currLevel.Head = 0 // start reading from 0 182 | // reset buffer for next level 183 | nextLevel.Data = nextLevel.Data[:maxSize:maxSize] // resize the buffer to `maxSize` elements. We don't care what's in it, because... 184 | nextLevel.Head = 0 // ... we start writing to index 0. 185 | } 186 | ds := DijkstraState{ 187 | Parents: parents, 188 | Dists: dists, 189 | Pathcounts: pathcounts, 190 | } 191 | return ds 192 | } 193 | -------------------------------------------------------------------------------- /persistence.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "os" 5 | "unsafe" 6 | 7 | mmap "github.com/edsrzf/mmap-go" 8 | ) 9 | 10 | // raw defines a struct for binary SimpleGraphs format. 11 | type raw struct { 12 | file *os.File 13 | data mmap.MMap 14 | 15 | FIndicesLen uint64 16 | FIndPtrLen uint64 17 | BIndicesLen uint64 18 | BIndPtrLen uint64 19 | 20 | FIndPtr []uint64 21 | FIndices []uint32 22 | 23 | BIndPtr []uint64 24 | BIndices []uint32 25 | } 26 | 27 | func (raw *raw) close() error { 28 | var err1, err2 error 29 | if raw.data != nil { 30 | err1 = raw.data.Unmap() 31 | } 32 | if raw.file != nil { 33 | err2 = raw.file.Close() 34 | } 35 | if err1 != nil { 36 | return err1 37 | } 38 | return err2 39 | } 40 | 41 | func loadRaw(filename string) (*raw, error) { 42 | var err error 43 | raw := &raw{} 44 | 45 | raw.file, err = os.OpenFile(filename, os.O_RDONLY, 0644) 46 | if err != nil { 47 | raw.close() 48 | return nil, err 49 | } 50 | 51 | raw.data, err = mmap.Map(raw.file, mmap.RDONLY, 0) 52 | if err != nil { 53 | raw.close() 54 | return nil, err 55 | } 56 | 57 | x := 0 58 | copy((*[8]byte)(unsafe.Pointer(&raw.FIndPtrLen))[:], raw.data[x:x+8]) 59 | x += 8 60 | copy((*[8]byte)(unsafe.Pointer(&raw.FIndicesLen))[:], raw.data[x:x+8]) 61 | x += 8 62 | 63 | copy((*[8]byte)(unsafe.Pointer(&raw.BIndPtrLen))[:], raw.data[x:x+8]) 64 | x += 8 65 | copy((*[8]byte)(unsafe.Pointer(&raw.BIndicesLen))[:], raw.data[x:x+8]) 66 | x += 8 67 | 68 | raw.FIndPtr = ((*[1 << 40]uint64)(unsafe.Pointer(&raw.data[x])))[0:int(raw.FIndPtrLen)] 69 | x += 8 * int(raw.FIndPtrLen) 70 | raw.FIndices = ((*[1 << 40]uint32)(unsafe.Pointer(&raw.data[x])))[0:int(raw.FIndicesLen)] 71 | x += 4 * int(raw.FIndicesLen) 72 | 73 | raw.BIndPtr = ((*[1 << 40]uint64)(unsafe.Pointer(&raw.data[x])))[0:int(raw.BIndPtrLen)] 74 | x += 8 * int(raw.BIndPtrLen) 75 | raw.BIndices = ((*[1 << 40]uint32)(unsafe.Pointer(&raw.data[x])))[0:int(raw.BIndicesLen)] 76 | 77 | return raw, nil 78 | } 79 | 80 | // Save saves a SimpleGraph to a file in raw (binary) format. 81 | func (g SimpleGraph) Save(filename string) error { 82 | FIndPtr := g.FMat().IndPtr 83 | FIndices := g.FMat().Indices 84 | BIndPtr := g.BMat().IndPtr 85 | BIndices := g.BMat().Indices 86 | 87 | output, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644) 88 | if err != nil { 89 | return err 90 | } 91 | defer output.Close() 92 | 93 | FIndPtrLen := int64(len(FIndPtr)) 94 | FIndicesLen := int64(len(FIndices)) 95 | BIndPtrLen := int64(len(BIndPtr)) 96 | BIndicesLen := int64(len(BIndices)) 97 | 98 | FIndPtrBytes := 8 * len(FIndPtr) 99 | FIndicesBytes := 4 * len(FIndices) 100 | BIndPtrBytes := 8 * len(BIndPtr) 101 | BIndicesBytes := 4 * len(BIndices) 102 | 103 | err = output.Truncate(int64(8 + 8 + 8 + 8 + FIndPtrBytes + FIndicesBytes + BIndPtrBytes + BIndicesBytes)) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | data, err := mmap.Map(output, mmap.RDWR, 0) 109 | if err != nil { 110 | return err 111 | } 112 | defer data.Unmap() 113 | 114 | x := 0 115 | 116 | copy(data[x:x+8], ((*[8]byte)(unsafe.Pointer(&FIndPtrLen))[:])) 117 | x += 8 118 | copy(data[x:x+8], ((*[8]byte)(unsafe.Pointer(&FIndicesLen))[:])) 119 | x += 8 120 | 121 | copy(data[x:x+8], ((*[8]byte)(unsafe.Pointer(&BIndPtrLen))[:])) 122 | x += 8 123 | copy(data[x:x+8], ((*[8]byte)(unsafe.Pointer(&BIndicesLen))[:])) 124 | x += 8 125 | 126 | if len(FIndPtr) > 0 { 127 | copy(data[x:x+FIndPtrBytes], 128 | ((*[1 << 40]byte)(unsafe.Pointer(&FIndPtr[0]))[:FIndPtrBytes])) 129 | x += FIndPtrBytes 130 | } 131 | if len(FIndices) > 0 { 132 | copy(data[x:x+FIndicesBytes], 133 | ((*[1 << 40]byte)(unsafe.Pointer(&FIndices[0]))[:FIndicesBytes])) 134 | x += FIndicesBytes 135 | } 136 | 137 | if len(FIndPtr) > 0 { 138 | copy(data[x:x+BIndPtrBytes], 139 | ((*[1 << 40]byte)(unsafe.Pointer(&BIndPtr[0]))[:BIndPtrBytes])) 140 | x += BIndPtrBytes 141 | } 142 | if len(BIndices) > 0 { 143 | copy(data[x:x+BIndicesBytes], 144 | ((*[1 << 40]byte)(unsafe.Pointer(&BIndices[0]))[:BIndicesBytes])) 145 | x += BIndicesBytes 146 | } 147 | 148 | return nil 149 | } 150 | 151 | // Load loads a raw (binary) SimpleGraph file and returns a SimpleGraph. 152 | func Load(fn string) (SimpleGraph, error) { 153 | raw, err := loadRaw(fn) 154 | if err != nil { 155 | return SimpleGraph{}, err 156 | } 157 | 158 | find := make([]uint32, raw.FIndicesLen) 159 | findptr := make([]uint64, raw.FIndPtrLen) 160 | bind := make([]uint32, raw.BIndicesLen) 161 | bindptr := make([]uint64, raw.BIndPtrLen) 162 | copy(find, raw.FIndices) 163 | copy(findptr, raw.FIndPtr) 164 | copy(bind, raw.BIndices) 165 | copy(bindptr, raw.BIndPtr) 166 | return FromRaw(findptr, find, bindptr, bind) 167 | } 168 | -------------------------------------------------------------------------------- /simpleedge.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "fmt" 4 | 5 | // SimpleEdge is a graph edge. 6 | type SimpleEdge struct { 7 | src uint32 8 | dst uint32 9 | } 10 | 11 | func (e SimpleEdge) Src() uint32 { 12 | return e.src 13 | } 14 | 15 | func (e SimpleEdge) Dst() uint32 { 16 | return e.dst 17 | } 18 | 19 | func (e SimpleEdge) String() string { 20 | return fmt.Sprintf("SimpleEdge %d -> %d", e.Src(), e.Dst()) 21 | } 22 | 23 | // Reverse reverses a SimpleEdge. 24 | func Reverse(e SimpleEdge) Edge { 25 | return SimpleEdge{e.dst, e.src} 26 | } 27 | -------------------------------------------------------------------------------- /simpleedgeiter.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "github.com/sbromberger/graphmatrix" 4 | 5 | type SimpleEdgeIter struct { 6 | mxiter graphmatrix.NZIter 7 | } 8 | 9 | func (it *SimpleEdgeIter) Next() Edge { 10 | r, c, _ := it.mxiter.Next() 11 | return SimpleEdge{src: r, dst: c} 12 | } 13 | 14 | func (it *SimpleEdgeIter) Done() bool { 15 | return it.mxiter.Done() 16 | } 17 | -------------------------------------------------------------------------------- /simplegraph.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sbromberger/graphmatrix" 7 | ) 8 | 9 | // SimpleGraph is a graph structure representing an undirected graph. 10 | type SimpleGraph struct { 11 | fmx, bmx graphmatrix.GraphMatrix 12 | } 13 | 14 | func (g SimpleGraph) String() string { 15 | return fmt.Sprintf("(%d, %d) graph", g.NumVertices(), g.NumEdges()) 16 | } 17 | 18 | // MakeSimpleGraph creates an undirected graph from vectors of source and dest vertices. 19 | func New(ss, ds []uint32) (SimpleGraph, error) { 20 | rSs := make([]uint32, len(ss)) 21 | rDs := make([]uint32, len(ds)) 22 | copy(rSs, ss) 23 | copy(rDs, ds) 24 | 25 | err := graphmatrix.SortIJ(&ss, &ds) 26 | if err != nil { 27 | return SimpleGraph{}, err 28 | } 29 | err = graphmatrix.SortIJ(&rDs, &rSs) 30 | if err != nil { 31 | return SimpleGraph{}, err 32 | } 33 | fmx, err := graphmatrix.NewFromSortedIJ(ss, ds) 34 | if err != nil { 35 | return SimpleGraph{}, err 36 | } 37 | bmx, err := graphmatrix.NewFromSortedIJ(rDs, rSs) 38 | if err != nil { 39 | return SimpleGraph{}, err 40 | } 41 | return SimpleGraph{fmx: fmx, bmx: bmx}, nil 42 | } 43 | 44 | func NewFromEdgeList(l EdgeList) (SimpleGraph, error) { 45 | ss := make([]uint32, len(l)) 46 | ds := make([]uint32, len(l)) 47 | for i, e := range l { 48 | ss[i] = e.Src() 49 | ds[i] = e.Dst() 50 | } 51 | return New(ss, ds) 52 | } 53 | 54 | // OutDegree returns the out degree of vertex u. 55 | func (g SimpleGraph) OutDegree(u uint32) uint32 { return uint32(len(g.OutNeighbors(u))) } 56 | 57 | // InDegree returns the indegree of vertex u. 58 | func (g SimpleGraph) InDegree(u uint32) uint32 { return uint32(len(g.InNeighbors(u))) } 59 | 60 | // OutNeighbors returns the out neighbors of vertex u. 61 | func (g SimpleGraph) OutNeighbors(u uint32) []uint32 { 62 | r, _ := g.fmx.GetRow(u) 63 | return r 64 | } 65 | 66 | // InNeighbors returns the in neighbors of vertex u. 67 | func (g SimpleGraph) InNeighbors(u uint32) []uint32 { 68 | r, _ := g.bmx.GetRow(u) 69 | return r 70 | } 71 | 72 | // HasEdge returns true if an edge exists between u and v. 73 | func (g SimpleGraph) HasEdge(u, v uint32) bool { 74 | un := g.OutNeighbors(u) 75 | vn := g.InNeighbors(v) 76 | lenun := uint64(len(un)) 77 | lenvn := uint64(len(vn)) 78 | var found bool 79 | if lenvn > lenun { 80 | _, found = graphmatrix.SearchSorted32(un, v, 0, lenun) 81 | } else { 82 | _, found = graphmatrix.SearchSorted32(vn, u, 0, lenvn) 83 | } 84 | return found 85 | } 86 | 87 | // AddEdge adds an edge to graph g 88 | func (g *SimpleGraph) AddEdge(u, v uint32) error { 89 | if err := g.fmx.SetIndex(u, v); err != nil { 90 | return err 91 | } 92 | if err := g.bmx.SetIndex(v, u); err != nil { 93 | return err 94 | } 95 | return nil 96 | } 97 | 98 | // NumEdges returns the number of edges 99 | func (g SimpleGraph) NumEdges() uint64 { 100 | return g.fmx.N() 101 | } 102 | 103 | // NumVertices returns the number of vertices 104 | func (g SimpleGraph) NumVertices() uint32 { 105 | return g.fmx.Dim() 106 | } 107 | 108 | // Edges returns an iterator of edges 109 | func (g SimpleGraph) Edges() EdgeIter { 110 | return &SimpleEdgeIter{mxiter: g.fmx.NewNZIter()} 111 | } 112 | 113 | // FMat returns the forward matrix of the graph. 114 | func (g SimpleGraph) FMat() graphmatrix.GraphMatrix { 115 | return g.fmx 116 | } 117 | 118 | // BMat returns the backward matrix of the graph. 119 | func (g SimpleGraph) BMat() graphmatrix.GraphMatrix { 120 | return g.bmx 121 | } 122 | 123 | func (g SimpleGraph) IsDirected() bool { return false } 124 | 125 | func FromRaw(findptr []uint64, find []uint32, bindptr []uint64, bind []uint32) (SimpleGraph, error) { 126 | fmx := graphmatrix.GraphMatrix{IndPtr: findptr, Indices: find} 127 | bmx := graphmatrix.GraphMatrix{IndPtr: bindptr, Indices: bind} 128 | return SimpleGraph{fmx, bmx}, nil 129 | } 130 | -------------------------------------------------------------------------------- /simplevertexiter.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | type SimpleVertexIter struct { 4 | curr uint32 5 | max uint32 6 | } 7 | 8 | func (it *SimpleVertexIter) Next() uint32 { 9 | it.curr++ 10 | if it.curr > it.max { 11 | it.curr = it.max 12 | } 13 | return it.curr 14 | } 15 | 16 | func (it *SimpleVertexIter) Done() bool { 17 | return it.curr > it.max 18 | } 19 | -------------------------------------------------------------------------------- /smallgraphs.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "errors" 4 | 5 | // HouseGraph returns an undirected house graph. 6 | func HouseGraph() SimpleGraph { 7 | ss := []uint32{1, 1, 2, 3, 3, 4} 8 | sd := []uint32{2, 3, 4, 4, 5, 5} 9 | g, _ := New(ss, sd) 10 | return g 11 | } 12 | 13 | func PathGraph(n int) (SimpleGraph, error) { 14 | ss := make([]uint32, n-1) 15 | sd := make([]uint32, n-1) 16 | for i := u0; i < uint32(n-1); i++ { 17 | ss[i] = i 18 | sd[i] = i + 1 19 | } 20 | return New(ss, sd) 21 | } 22 | 23 | func CycleGraph(n int) (SimpleGraph, error) { 24 | ss := make([]uint32, n) 25 | sd := make([]uint32, n) 26 | for i := u0; i < uint32(n-1); i++ { 27 | ss[i] = i 28 | sd[i] = i + 1 29 | } 30 | ss[n-1] = uint32(n - 1) 31 | sd[n-1] = 0 32 | 33 | return New(ss, sd) 34 | } 35 | 36 | func WheelGraph(n int) (SimpleGraph, error) { 37 | if n < 3 { 38 | return SimpleGraph{}, errors.New("WheelGraph must have at least 3 vertices") 39 | } 40 | ss := make([]uint32, 2*(n-1)) 41 | sd := make([]uint32, 2*(n-1)) 42 | n32 := uint32(n) 43 | for i := u0; i < uint32(n-1); i++ { 44 | ss[i] = 0 45 | sd[i] = i + 1 46 | if i == 0 { 47 | continue 48 | } 49 | ss[n32-1+i] = i 50 | sd[n32-1+i] = i + 1 51 | } 52 | ss[n-1] = uint32(n - 1) 53 | sd[n-1] = 1 54 | 55 | return New(ss, sd) 56 | } 57 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !*.go -------------------------------------------------------------------------------- /test/processuints.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sbromberger/gographs/persistence" 7 | ) 8 | 9 | func main() { 10 | fn := os.Args[1] 11 | fn2 := os.Args[2] 12 | h := persistence.ReadText(fn) 13 | f := h.Fadj() 14 | raw.SaveRaw(fn2, f.Rowidx, f.Colptr) 15 | } 16 | -------------------------------------------------------------------------------- /test/testedgelist.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/sbromberger/gographs" 8 | ) 9 | 10 | func main() { 11 | fn := os.Args[1] 12 | persistene.GraphFromEdgeList(fn) 13 | h := gographs.HouseGraph() 14 | fmt.Println("h.Fadj = ", h.Fadj()) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /test/testgg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "runtime" 9 | "runtime/pprof" 10 | "time" 11 | 12 | "github.com/sbromberger/gographs/persistence" 13 | 14 | "github.com/sbromberger/gographs" 15 | ) 16 | 17 | var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 18 | var memprofile = flag.String("memprofile", "", "write memory profile to `file`") 19 | var fn = flag.String("f", "", "filename to open") 20 | 21 | func sum(a []int) int { 22 | s := 0 23 | for _, r := range a { 24 | s += r 25 | } 26 | return s 27 | } 28 | 29 | func main() { 30 | ggg := 94 31 | flag.Parse() 32 | if *cpuprofile != "" { 33 | f, err := os.Create(*cpuprofile) 34 | if err != nil { 35 | log.Fatal("could not create CPU profile: ", err) 36 | } 37 | fmt.Println("created cpu profile: ", *cpuprofile) 38 | if err := pprof.StartCPUProfile(f); err != nil { 39 | log.Fatal("could not start CPU profile: ", err) 40 | } 41 | defer pprof.StopCPUProfile() 42 | } 43 | // 0 3 6 44 | // 0 [ T F 45 | // 1 T F 46 | // 2 T T 47 | // 3 F T 48 | // 4 F T ] 49 | 50 | // HouseGraph 51 | // rind := []uint32{1, 2, 0, 3, 0, 3, 4, 1, 2, 4, 2, 3} 52 | // cptr := []uint32{0, 2, 4, 7, 10, 12} 53 | // g := gographs.MakeGraph(rind, cptr) 54 | 55 | // fmt.Println("g created") 56 | // fmt.Println("has_edge (g, 0, 1) = ", g.HasEdge(0, 1)) 57 | // fmt.Println("has_edge (g, 1, 0) = ", g.HasEdge(1, 0)) 58 | // fmt.Println("has_edge (g, 1, 2) = ", g.HasEdge(1, 2)) 59 | 60 | // fmt.Println("edges(g) = ", g.Edges()) 61 | // fmt.Println("outneighbors(g, 2) = ", g.OutNeighborsInt(2)) 62 | 63 | h := persistence.ReadStaticGraph(*fn) 64 | // fmt.Println("edges(h) = ", h.Edges()) 65 | // fmt.Println("Fadj(h) = ", h.Fadj()) 66 | fmt.Println("Order(h) = ", h.Order()) 67 | fmt.Println("Size(h) = ", h.Size()) 68 | start := time.Now() 69 | gographs.Dijkstra(&h, 0) 70 | elapsed := time.Since(start) 71 | fmt.Println("dijkstra done:") 72 | fmt.Println(elapsed) 73 | 74 | // fmt.Println("doing all dijkstras") 75 | // start = time.Now() 76 | // ds := gographs.AllDijkstraShortestPaths(&h) 77 | // elapsed = time.Since(start) 78 | // fmt.Println("elapsed = ", elapsed) 79 | // fmt.Println("len(ds) = ", len(ds)) 80 | // // fmt.Println("len(ds[5999].Dists) = ", len(ds[5999].Dists)) 81 | if *memprofile != "" { 82 | f, err := os.Create(*memprofile) 83 | if err != nil { 84 | log.Fatal("could not create memory profile: ", err) 85 | } 86 | fmt.Println("made mem profile", *memprofile) 87 | runtime.GC() // get up-to-date statistics 88 | if err := pprof.WriteHeapProfile(f); err != nil { 89 | log.Fatal("could not write memory profile: ", err) 90 | } 91 | f.Close() 92 | } 93 | 94 | } 95 | 96 | // fmt.Println("d.dists[10:20] = ", d.Dists) 97 | // fmt.Println("d.parents[20:30] = ", d.Parents) 98 | // fmt.Println("d.preds[30:40] = ", d.Predecessors) 99 | // fmt.Println("d.pathcounts[50:60] = ", d.Pathcounts) 100 | // fmt.Println("sum d.dists = ", sum(d.Dists)) 101 | // fmt.Println("sum d.pathcounts = ", sum(d.Pathcounts)) 102 | // z := sparsevecs.UInt32SparseVec{v1, v2} 103 | // fmt.Println("z.rowidx = ", z.Rowidx) 104 | // fmt.Println("test1: ", z.GetIndexInt(3, 0)) // F 105 | // fmt.Println("test2: ", z.GetIndexInt(2, 1)) // T 106 | // fmt.Println("test3: ", z.GetIndexInt(1, 0)) // T 107 | // fmt.Println("-----------------------------------------") 108 | // fmt.Println("ok, testing insert") 109 | // fmt.Println("test4:") 110 | // z.Insert(2, 1) 111 | // // 0 3 6 112 | // // 0 [ T F 113 | // // 1 T F 114 | // // 2 T T 115 | // // 3 F T 116 | // // 4 F T ] 117 | // fmt.Println("-----------------------------------------") 118 | // fmt.Println("test5: ", z) 119 | // z.Insert(2, 2) 120 | // // 0 3 6 7 121 | // // 0 [ T F F 122 | // // 1 T F F 123 | // // 2 T T T 124 | // // 3 F T F 125 | // // 4 F T F ] 126 | // // 0 1 2 / 2 3 4 / 2 127 | // fmt.Println("test6: ", z) 128 | -------------------------------------------------------------------------------- /test/testmut.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func mutit(j []int) { 6 | for i := range j { 7 | j[i] = 44 8 | } 9 | } 10 | func main() { 11 | j := make([]int, 4) 12 | fmt.Println("j = ", j) 13 | mutit(j) 14 | fmt.Println("j = ", j) 15 | } 16 | -------------------------------------------------------------------------------- /test/testptr.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Foo struct { 6 | a []uint32 7 | x int 8 | } 9 | 10 | func newFoo(n int) Foo { 11 | a := make([]uint32, 0, n) 12 | return Foo{a, n} 13 | } 14 | 15 | func doFoo(f Foo) { 16 | f.a = append(f.a, 20) 17 | f.x = 20 18 | } 19 | 20 | func doFooP(f *Foo) { 21 | f.a = append(f.a, 30) 22 | f.x = 30 23 | } 24 | 25 | func main() { 26 | f := newFoo(10) 27 | f.a = append(f.a, 10) 28 | fmt.Println("f = ", f) 29 | doFoo(f) 30 | fmt.Println("f = ", f) 31 | doFooP(&f) 32 | fmt.Println("f = ", f) 33 | } 34 | -------------------------------------------------------------------------------- /test/testsortpairs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type Pair struct { 9 | src int 10 | dst int 11 | } 12 | 13 | type Pairs []Pair 14 | 15 | func (p Pairs) Len() int { 16 | return len(p) 17 | } 18 | 19 | func (p Pairs) Swap(i, j int) { 20 | p[i], p[j] = p[j], p[i] 21 | } 22 | 23 | func (p Pairs) Less(i, j int) bool { 24 | if p[i].src < p[j].src { 25 | return true 26 | } 27 | if p[i].src > p[j].src { 28 | return false 29 | } 30 | return p[i].dst < p[j].dst 31 | } 32 | 33 | func main() { 34 | a := make([]Pair, 0) 35 | a = append(a, Pair{1, 2}) 36 | a = append(a, Pair{2, 20}) 37 | a = append(a, Pair{2, 10}) 38 | a = append(a, Pair{1, 60}) 39 | 40 | sort.Sort(Pairs(a)) 41 | fmt.Println(a) 42 | } 43 | --------------------------------------------------------------------------------