├── .travis.yml ├── LICENSE ├── README.md ├── builder.go ├── builder_test.go ├── dfs ├── dfs.go └── dfs_test.go ├── doc.go ├── doc ├── av.dot ├── av.dot.png ├── base.dot ├── base.dot.png ├── each_predecessor.dot ├── each_predecessor.dot.png ├── each_successor.dot ├── each_successor.dot.png ├── eaf.dot ├── eaf.dot.png ├── eat.dot ├── eat.dot.png ├── ee.dot ├── ee.dot.png ├── eeit.dot ├── eeit.dot.png ├── ev.dot ├── ev.dot.png └── gen-all.sh ├── edge.go ├── edge_list.go ├── edge_test.go ├── graph.go ├── graph └── al │ ├── al.go │ ├── al_shared.go │ ├── al_test.go │ ├── data.go │ ├── directed.go │ ├── graph_bench_test.go │ ├── labeled.go │ ├── undirected.go │ └── weighted.go ├── null.go ├── null_test.go ├── rand ├── bernoulli.go └── bernoulli_test.go ├── spec ├── graph_spec.go ├── literal.go ├── suite_basicmut.go ├── suite_count.go ├── suite_data.go ├── suite_digraph.go ├── suite_graph.go ├── suite_label.go ├── suite_simple.go └── suite_weighted.go ├── test-coverage.sh ├── util.go ├── util_test.go ├── vertex.go └── wercker.yml /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.2 4 | - 1.3 5 | - tip 6 | install: 7 | - go get gopkg.in/fatih/set.v0 8 | - go get github.com/lann/builder 9 | - go get github.com/kr/pretty 10 | - go get -t github.com/sdboyer/gocheck 11 | script: go test -v ./... -gocheck.v 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015 Sam Boyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gogl 2 | 3 | [![Build Status](https://travis-ci.org/sdboyer/gogl.png?branch=master)](https://travis-ci.org/sdboyer/gogl) 4 | [![Coverage Status](https://img.shields.io/coveralls/sdboyer/gogl.svg)](https://coveralls.io/r/sdboyer/gogl?branch=master) 5 | 6 | gogl is a graph library in Go. Its goal is to provide simple, unifying interfaces and implementations of graph algorithms and datastructures that can scale from small graphs to very large graphs. The latter case is, as yet, untested! 7 | 8 | gogl is based on the premise that working with graphs can be [decomplected](http://www.infoq.com/presentations/Simple-Made-Easy) by focusing primarily on the natural constraints established in graph theory. 9 | 10 | There's still a lot to do - gogl is still firming up significant aspects of how its API works. 11 | 12 | ## Principles 13 | 14 | Graph systems are often big, complicated affairs. Some of it comes with the territory - graphs are not a simple datastructure. But gogl tries to be not that, mostly by focusing on getting the basics right. These are gogl's operant principles: 15 | 16 | 1. Simplicity: fully and correctly modeling graph theoretic concepts in idiomatic Go. 17 | 1. Performance: be as fast as design constraints and known-best algorithms allow. 18 | 1. Extensibility: expect others to run gogl's graph datastructures through their own algorithms, , and gogl's algorithms with their graph implementations. 19 | 1. Functional: orient towards transforms, functors, and streams; achieve other styles through layering. 20 | 1. Flexibility: Be unopinionated about vertices, and minimally opinionated about edges. 21 | 1. Correctness: Utilize [commonly accepted graph terminology](http://en.wikipedia.org/wiki/Glossary_of_graph_theory) where possible, and adhere to its meaning. 22 | 23 | The first and last points are key - names in gogl are carefully chosen, with the hope that they can guide intuition when stricter rules (e.g., the type system) become ambiguous. The [godoc](https://godoc.org/github.com/sdboyer/gogl) generally takes care to detail these subtleties. But godoc is a reference, not a tutorial. 24 | 25 | ## Quickstart 26 | 27 | Getting started with gogl is simple: create a graph object, add your data, and off you go. 28 | 29 | ```go 30 | package main 31 | 32 | import ( 33 | "fmt" 34 | "github.com/sdboyer/gogl" 35 | "github.com/sdboyer/gogl/graph/al" 36 | "github.com/sdboyer/gogl/dfs" 37 | ) 38 | 39 | func main() { 40 | // gogl uses a builder to specify the kind of graph you want. 41 | graph := gogl.Spec(). 42 | // The graph should be mutable. Default is immutable. 43 | Mutable(). 44 | // The graph should have directed edges (arcs). Default is undirected. 45 | Directed(). 46 | // The graph's edges are plain - no labels, weights, etc. This is the default. 47 | Basic(). 48 | // No loops or parallel edges. This is the default. 49 | SimpleGraph(). 50 | // al.G picks and returns an adjacency list-based graph, based on the spec. 51 | Create(al.G). 52 | // The builder always returns a Graph; type assert to get access to add/remove methods. 53 | (gogl.MutableGraph) 54 | 55 | // Adds two basic edges. Of course, this adds the vertices, too. 56 | graph.AddEdges(gogl.NewEdge("foo", "bar"), gogl.NewEdge("bar", "baz")) 57 | 58 | // gogl's core iteration concept is built on injected functions (VertexStep or 59 | // EdgeStep). Here, a VertexStep is called once per vertex in the graph; 60 | // the return value determines whether traversal continues. 61 | graph.EachVertex(func(v gogl.Vertex) (terminate bool) { 62 | fmt.Println(v) // Probably "foo\nbar\nbaz", but ordering is not guaranteed. 63 | return // returns false, so iteration continues 64 | }) 65 | 66 | // gogl refers to these sorts of iterating methods as enumerators. There are four 67 | // such methods on undirected graphs, and four more on directed graphs. 68 | 69 | // If you know you need the full result set, gogl provides functors to collect enumerations 70 | // into slices. This makes ranging easy. 71 | var vertices []gogl.Vertex = gogl.CollectVertices(graph) 72 | for _, v := range vertices { 73 | fmt.Println(v) // same as with EachVertex(). 74 | } 75 | 76 | // The pattern is the same with edge enumeration. These two have the same output: 77 | graph.EachEdge(func(e gogl.Edge) (terminate bool) { 78 | fmt.Println(e) // Probably "{foo bar}\n{bar baz}". Again, ordering is not guaranteed. 79 | return 80 | }) 81 | for _, e := range gogl.CollectEdges(graph) { 82 | fmt.Println(e) 83 | } 84 | 85 | // gogl's algorithms all rely on these enumerators to do their work. Here, we use 86 | // a depth-first topological sort algorithm to produce a slice of vertices. 87 | var tsl []gogl.Vertex 88 | tsl, err := dfs.Toposort(graph, "foo") 89 | if err == nil { 90 | fmt.Println(tsl) // [baz bar foo] 91 | } 92 | } 93 | ``` 94 | 95 | ## Enumerators 96 | 97 | Enumerators are the primary means by which gogl graphs are expressed. As shown in the Quickstart section, they are methods on graph datastructures that receive a 'step' function, and call that function once per datum (Vertex or Edge) that is found as the method traverses the graph. There are four enumerators for gogl's undirected graphs, and four additional ones for directed graphs. 98 | 99 | We need an example in order to demonstrate the behavior of the enumerators, so, here's the code to create a graph: 100 | 101 | ```go 102 | func main() { 103 | graph := gogl.G().Mutable().Directed().Create(gogl.AdjacencyList) 104 | 105 | graph.AddEdges([]gogl.Edge{ 106 | NewEdge("a", "b"), 107 | NewEdge("b", "c"), 108 | NewEdge("a", "c"), 109 | NewEdge("a", "c"), 110 | NewEdge("d", "a"), 111 | NewEdge("d", "e"), 112 | }) 113 | 114 | // 'f' is a vertex isolate. 115 | graph.EnsureVertex("f") 116 | } 117 | ``` 118 | 119 | And here's a visual representation of that same graph: 120 | 121 | ![Base graph](doc/base.dot.png) 122 | 123 | Enumerators mostly come in pairs - for each type of relationship or listing, you can receive either the vertex or the edge. 124 | 125 | ### All the things 126 | 127 | The first two enumerators, `EachVertex()` and `EachEdge()`, traverse the entire graph. Calling `EachVertex()` would result in six calls to the injected step function, one for each of the contained vertices (marked in blue). 128 | 129 | ![EachVertex()](doc/ev.dot.png) 130 | 131 | Calling `EachEdge()` will call the injected step six times, once for each of the contained edges: 132 | 133 | ![EachEdge()](doc/ee.dot.png) 134 | 135 | *Note that on directed graphs, `EachArc()` can also be used to this same effect; it simply passes the edge object with `Arc` interface type instead of `Edge`.* 136 | 137 | An important guarantee of enumerators not necessarily implied by the interface is that they call the step function **exactly** once for each relevant datum. Client code should never have to deduplicate data. 138 | 139 | Also, remember that while implementations must enumerate all the elements exactly once, gogl has no requirement as to ordering. Graph implementations are free to sort the results, or not, as they choose. 140 | 141 | ### Adjacency and Incidence 142 | 143 | The next pair, `EachAdjacentTo()` and `EachEdgeIncidentTo()`, are used to visit a given vertex's immediate neighbors. 144 | 145 | `EachAdjacentTo()` traverses a vertex's "adjacent" vertices, which are defined as vertices that share an edge with the given vertex. Edge directionality, if any, is irrelevant. In our sample graph, calling `EachAdjacentTo("a")` will result in three calls to the injected step. 146 | 147 | ![EachAdjacentTo("a")](doc/av.dot.png) 148 | 149 | `EachEdgeIncidentTo()` enumerates all edges incident to the provided vertex. An edge is incident to a vertex if that vertex is either one of the edge's two connected endpoints; this is just an edge-oriented way of looking at that same adjacency relationship. Edge directionality, if any, is once again irrelevant. 150 | 151 | ![EachEdgeIncidentTo("a")](doc/eeit.dot.png) 152 | 153 | ### Digraphs: Outbounds and Successors 154 | 155 | The other four enumerators apply only to directed graphs, as they are cognizant of edge directionality. The first pair, `EachSuccessorOf()` and `EachArcFrom()`, deal with outbound edges from a given vertex. 156 | 157 | `EachSuccessorOf()` enumerates all of a given vertex's "successors" - vertices which are the target, or head, of a directed edge where the given vertex is the tail, or source. 158 | 159 | ![EachSuccessorOf("a")](doc/each_successor.dot.png) 160 | 161 | `EachArcFrom()` enumerates all the outbound arcs ("arc" simply indicates an edge is directed) from the given vertex: 162 | 163 | ![EachArcFrom("a")](doc/eaf.dot.png) 164 | 165 | ### Digraphs: Inbounds and Predecessors 166 | 167 | The final pair, `EachPredecessorOf()` and `EachArcTo()`, should be easy enough to guess at. `EachPredecessorOf()` enumerates all of a vertex's predecessors. 168 | 169 | ![EachPredecessorOf("a")](doc/each_predecessor.dot.png) 170 | 171 | And `EachArcTo()` enumerates all the arcs inbound to the given vertex: 172 | 173 | ![EachArcTo("a")](doc/eat.dot.png) 174 | 175 | `EachPredecessorOf()` and `EachSuccessorOf()` are the logical complements; calling both is equivalent to calling `EachAdjacentTo()`. The same is true of the arc methods and `EachEdgeIncidentTo()`. 176 | 177 | These eight enumerators are gogl's most important building blocks. They fully describe the basic structure of a graph-based model, and can be combined to formulate essentially any basic graph visitation pattern. 178 | 179 | There are some additional enumerators for graph subtypes - e.g., `EachLabeledEdgeIncidentTo()` (not yet implemented) is an "optional optimization" enumerator that labeled graphs can implement if, say, they maintain indices that allow them to more efficiently locate and return the subset of incident edges with a particular label than would a naive traversal of the entire incident edge set with direct string comparisons. 180 | 181 | Where possible, such optional optimizations are automatically selected and utilized by gogl's various functors. 182 | 183 | ## License 184 | 185 | MIT 186 | -------------------------------------------------------------------------------- /builder.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | /* Graph type constants. Used primarily for specs. */ 4 | 5 | // Describes the properties of a graph as a bitfield. 6 | type GraphProperties uint16 7 | 8 | const ( 9 | // Edge directedness. Flags are provided for both, though gogl does not really support 10 | // a hybrid graph containing both directed and undirected edges. Algorithms would have 11 | // undefined results. 12 | G_UNDIRECTED = 1 << iota 13 | G_DIRECTED 14 | 15 | // Edge type. Basic (untyped edges, represented solely by the Edge interface) is the implied zero-value. 16 | G_BASIC 17 | G_LABELED 18 | G_WEIGHTED 19 | G_DATA 20 | 21 | // Multiplicity. Simple (no loops or multiple edges) is the implied zero-value. 22 | G_SIMPLE 23 | G_LOOPS 24 | G_PARALLEL 25 | 26 | // Mutability. Immutable is the implied zero-value. 27 | G_IMMUTABLE 28 | G_MUTABLE 29 | G_PERSISTENT = 1< b -> c; 5 | a -> c; 6 | d -> a; 7 | d -> e; 8 | a [fontcolor="black",style=filled,color="#F38630"]; 9 | b [fontcolor="black",style=filled,color="#23A7C0"]; 10 | c [fontcolor="black",style=filled,color="#23A7C0"]; 11 | d [fontcolor="black",style=filled,color="#23A7C0"]; 12 | f; 13 | label="EachAdjacentTo(\"a\")" 14 | } 15 | -------------------------------------------------------------------------------- /doc/av.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/av.dot.png -------------------------------------------------------------------------------- /doc/base.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | a -> b -> c; 3 | a -> c; 4 | d -> a; 5 | d -> e; 6 | f; 7 | } 8 | -------------------------------------------------------------------------------- /doc/base.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/base.dot.png -------------------------------------------------------------------------------- /doc/each_predecessor.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [color="grey"] 4 | a -> b -> c; 5 | a -> c; 6 | d -> a; 7 | d -> e; 8 | a [fontcolor="black",style=filled,color="#F38630"]; 9 | b; 10 | c; 11 | d [fontcolor="black",style=filled,color="#23A7C0"]; 12 | f; 13 | label="EachPredecessorOf(\"a\")" 14 | } 15 | -------------------------------------------------------------------------------- /doc/each_predecessor.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/each_predecessor.dot.png -------------------------------------------------------------------------------- /doc/each_successor.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [color="grey"] 4 | a -> b -> c; 5 | a -> c; 6 | d -> a; 7 | d -> e; 8 | a [fontcolor="black",style=filled,color="#F38630"]; 9 | b [fontcolor="black",style=filled,color="#23A7C0"]; 10 | c [fontcolor="black",style=filled,color="#23A7C0"]; 11 | d; 12 | f; 13 | label="EachSuccessorOf(\"a\")" 14 | } 15 | -------------------------------------------------------------------------------- /doc/each_successor.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/each_successor.dot.png -------------------------------------------------------------------------------- /doc/eaf.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [color="grey"] 4 | a -> b [style=bold,color="#23A7C0"]; 5 | b -> c; 6 | a -> c [style=bold,color="#23A7C0"]; 7 | d -> a; 8 | d -> e; 9 | a [fontcolor="black",style=filled,color="#F38630"]; 10 | f; 11 | label="EachArcFrom(\"a\")" 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /doc/eaf.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/eaf.dot.png -------------------------------------------------------------------------------- /doc/eat.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [color="grey"] 4 | a -> b; 5 | b -> c; 6 | a -> c; 7 | d -> a [style=bold,color="#23A7C0"]; 8 | d -> e; 9 | a [fontcolor="black",style=filled,color="#F38630"]; 10 | f; 11 | label="EachArcTo(\"a\")" 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /doc/eat.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/eat.dot.png -------------------------------------------------------------------------------- /doc/ee.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [style=bold,color="grey"] 4 | a -> b [color="#23A7C0"]; 5 | b -> c [color="#23A7C0"]; 6 | a -> c [color="#23A7C0"]; 7 | d -> a [color="#23A7C0"]; 8 | d -> e [color="#23A7C0"]; 9 | f; 10 | label="EachEdge()" 11 | } 12 | -------------------------------------------------------------------------------- /doc/ee.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/ee.dot.png -------------------------------------------------------------------------------- /doc/eeit.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey",fontcolor="grey"] 3 | edge [color="grey"] 4 | a -> b [style=bold,color="#23A7C0"]; 5 | b -> c; 6 | a -> c [style=bold,color="#23A7C0"]; 7 | d -> a [style=bold,color="#23A7C0"]; 8 | d -> e; 9 | a [fontcolor="black",style=filled,color="#F38630"]; 10 | f; 11 | label="EachEdgeIncidentTo(\"a\")" 12 | } 13 | -------------------------------------------------------------------------------- /doc/eeit.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/eeit.dot.png -------------------------------------------------------------------------------- /doc/ev.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | node [color="grey"] 3 | edge [color="grey"] 4 | a -> b -> c; 5 | a -> c; 6 | d -> a; 7 | d -> e; 8 | a [style=filled,color="#23A7C0"]; 9 | b [style=filled,color="#23A7C0"]; 10 | c [style=filled,color="#23A7C0"]; 11 | d [style=filled,color="#23A7C0"]; 12 | e [style=filled,color="#23A7C0"]; 13 | f [style=filled,color="#23A7C0"]; 14 | label="EachVertex()" 15 | } 16 | -------------------------------------------------------------------------------- /doc/ev.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdboyer/gogl/a7d439139054f2c3c7e02039f753286b0a7429d6/doc/ev.dot.png -------------------------------------------------------------------------------- /doc/gen-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | for FILE in `ls *.dot`; do circo -Tpng $FILE -O; open $FILE.png; done 3 | -------------------------------------------------------------------------------- /edge.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | /* Edge interfaces */ 4 | 5 | // A graph's behaviors are primarily a product of the constraints and 6 | // capabilities it places on its edges. These constraints and capabilities 7 | // determine whether certain types of operations are possible on the graph, as 8 | // well as the efficiencies for various operations. 9 | 10 | // gogl aims to provide a range of graph implementations that can meet 11 | // the varying constraints and implementation needs, but still achieve optimal 12 | // performance given those constraints. 13 | 14 | // Edge describes an undirected connection between two vertices. 15 | type Edge interface { 16 | Both() (u Vertex, v Vertex) // No order consistency is implied. 17 | } 18 | 19 | // Arc describes a directed connection between two vertices. 20 | type Arc interface { 21 | Both() (u Vertex, v Vertex) // u is tail/source, v is head/target. 22 | Source() Vertex 23 | Target() Vertex 24 | } 25 | 26 | // WeightedEdge describes an Edge that carries a numerical weight. 27 | type WeightedEdge interface { 28 | Edge 29 | Weight() float64 30 | } 31 | 32 | // WeightedArc describes an Arc that carries a numerical weight. 33 | type WeightedArc interface { 34 | Arc 35 | Weight() float64 36 | } 37 | 38 | // LabeledEdge describes an Edge that also has a string label. 39 | type LabeledEdge interface { 40 | Edge 41 | Label() string 42 | } 43 | 44 | // LabeledArc describes an Arc that also has a string label. 45 | type LabeledArc interface { 46 | Arc 47 | Label() string 48 | } 49 | 50 | // DataEdge describes an Edge that also holds arbitrary data. 51 | type DataEdge interface { 52 | Edge 53 | Data() interface{} 54 | } 55 | 56 | // DataArc describes an Arc that also holds arbitrary data. 57 | type DataArc interface { 58 | Arc 59 | Data() interface{} 60 | } 61 | 62 | /* Base implementations of Edge interfaces */ 63 | 64 | // BaseEdge is a struct used to represent edges and meet the Edge interface 65 | // requirements. It uses the standard graph notation, (U,V), for its 66 | // contained vertex pair. 67 | type baseEdge struct { 68 | u Vertex 69 | v Vertex 70 | } 71 | 72 | func (e baseEdge) Both() (Vertex, Vertex) { 73 | return e.u, e.v 74 | } 75 | 76 | // Create a new basic edge. 77 | func NewEdge(u, v Vertex) Edge { 78 | return baseEdge{u: u, v: v} 79 | } 80 | 81 | type baseArc struct { 82 | baseEdge 83 | } 84 | 85 | func (e baseArc) Source() Vertex { 86 | return e.u 87 | } 88 | 89 | func (e baseArc) Target() Vertex { 90 | return e.v 91 | } 92 | 93 | // Create a new basic arc. 94 | func NewArc(u, v Vertex) Arc { 95 | return baseArc{baseEdge{u: u, v: v}} 96 | } 97 | 98 | // BaseWeightedEdge extends BaseEdge with weight data. 99 | type baseWeightedEdge struct { 100 | baseEdge 101 | w float64 102 | } 103 | 104 | func (e baseWeightedEdge) Weight() float64 { 105 | return e.w 106 | } 107 | 108 | // Create a new weighted edge. 109 | func NewWeightedEdge(u, v Vertex, weight float64) WeightedEdge { 110 | return baseWeightedEdge{baseEdge{u: u, v: v}, weight} 111 | } 112 | 113 | type baseWeightedArc struct { 114 | baseArc 115 | w float64 116 | } 117 | 118 | func (e baseWeightedArc) Weight() float64 { 119 | return e.w 120 | } 121 | 122 | // Create a new weighted arc. 123 | func NewWeightedArc(u, v Vertex, weight float64) WeightedArc { 124 | return baseWeightedArc{baseArc{baseEdge{u: u, v: v}}, weight} 125 | } 126 | 127 | // BaseLabeledEdge extends BaseEdge with label data. 128 | type baseLabeledEdge struct { 129 | baseEdge 130 | l string 131 | } 132 | 133 | func (e baseLabeledEdge) Label() string { 134 | return e.l 135 | } 136 | 137 | // Create a new labeled edge. 138 | func NewLabeledEdge(u, v Vertex, label string) LabeledEdge { 139 | return baseLabeledEdge{baseEdge{u: u, v: v}, label} 140 | } 141 | 142 | // BaseLabeledArc extends BaseArc with label data. 143 | type baseLabeledArc struct { 144 | baseArc 145 | l string 146 | } 147 | 148 | func (e baseLabeledArc) Label() string { 149 | return e.l 150 | } 151 | 152 | // Create a new labeled arc. 153 | func NewLabeledArc(u, v Vertex, label string) LabeledArc { 154 | return baseLabeledArc{baseArc{baseEdge{u: u, v: v}}, label} 155 | } 156 | 157 | // BaseDataEdge extends BaseEdge with arbitrary data. 158 | type baseDataEdge struct { 159 | baseEdge 160 | d interface{} 161 | } 162 | 163 | func (e baseDataEdge) Data() interface{} { 164 | return e.d 165 | } 166 | 167 | // Create a new "data" edge - an edge with arbitrary embedded data. 168 | func NewDataEdge(u, v Vertex, data interface{}) DataEdge { 169 | return baseDataEdge{baseEdge{u: u, v: v}, data} 170 | } 171 | 172 | // BaseDataArc extends BaseArc with arbitrary data. 173 | type baseDataArc struct { 174 | baseArc 175 | d interface{} 176 | } 177 | 178 | func (e baseDataArc) Data() interface{} { 179 | return e.d 180 | } 181 | 182 | // Create a new "data" edge - an edge with arbitrary embedded data. 183 | func NewDataArc(u, v Vertex, data interface{}) DataArc { 184 | return baseDataArc{baseArc{baseEdge{u: u, v: v}}, data} 185 | } 186 | -------------------------------------------------------------------------------- /edge_list.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | import ( 4 | "gopkg.in/fatih/set.v0" 5 | ) 6 | 7 | // Shared helper function for edge lists to enumerate vertices. 8 | func elVertices(el interface{}, fn VertexStep) { 9 | set := set.New(set.NonThreadSafe) 10 | 11 | el.(EdgeEnumerator).Edges(func(e Edge) (terminate bool) { 12 | set.Add(e.Both()) 13 | return 14 | }) 15 | 16 | for _, v := range set.List() { 17 | if fn(v) { 18 | return 19 | } 20 | } 21 | } 22 | 23 | // An EdgeList is a naive GraphSource implementation that is backed only by an edge slice. 24 | // 25 | // EdgeLists are primarily intended for use as fixtures. 26 | // 27 | // It is inherently impossible for an EdgeList to represent a vertex isolate (degree 0) fully 28 | // correctly, as vertices are only described in the context of their edges. One can be a 29 | // little hacky, though, and represent one with a loop. As gogl expects graph implementations 30 | // to simply discard loops if they are disallowed by the graph's constraints (i.e., in simple 31 | // and multigraphs), they *should* be interpreted as vertex isolates. 32 | type EdgeList []Edge 33 | 34 | func (el EdgeList) Vertices(fn VertexStep) { 35 | elVertices(el, fn) 36 | } 37 | 38 | func (el EdgeList) Edges(fn EdgeStep) { 39 | for _, e := range el { 40 | if fn(e) { 41 | return 42 | } 43 | } 44 | } 45 | 46 | // An ArcList is a naive DigraphSource implementation that is backed only by an arc slice. 47 | type ArcList []Arc 48 | 49 | func (el ArcList) Vertices(fn VertexStep) { 50 | elVertices(el, fn) 51 | } 52 | 53 | func (el ArcList) Edges(fn EdgeStep) { 54 | for _, e := range el { 55 | if fn(e) { 56 | return 57 | } 58 | } 59 | } 60 | 61 | func (el ArcList) Arcs(fn ArcStep) { 62 | for _, e := range el { 63 | if fn(e) { 64 | return 65 | } 66 | } 67 | } 68 | 69 | // A WeightedEdgeList is a naive GraphSource implementation that is backed only by an edge slice. 70 | // 71 | // This variant is for weighted edges. 72 | type WeightedEdgeList []WeightedEdge 73 | 74 | func (el WeightedEdgeList) Vertices(fn VertexStep) { 75 | elVertices(el, fn) 76 | } 77 | 78 | func (el WeightedEdgeList) Edges(fn EdgeStep) { 79 | for _, e := range el { 80 | if fn(e) { 81 | return 82 | } 83 | } 84 | } 85 | 86 | // A WeightedArcList is a naive DigraphSource implementation that is backed only by an arc slice. 87 | type WeightedArcList []Arc 88 | 89 | func (el WeightedArcList) Vertices(fn VertexStep) { 90 | elVertices(el, fn) 91 | } 92 | 93 | func (el WeightedArcList) Edges(fn EdgeStep) { 94 | for _, e := range el { 95 | if fn(e) { 96 | return 97 | } 98 | } 99 | } 100 | 101 | func (el WeightedArcList) Arcs(fn ArcStep) { 102 | for _, e := range el { 103 | if fn(e) { 104 | return 105 | } 106 | } 107 | } 108 | 109 | // A LabeledEdgeList is a naive GraphSource implementation that is backed only by an edge slice. 110 | // 111 | // This variant is for labeled edges. 112 | type LabeledEdgeList []LabeledEdge 113 | 114 | func (el LabeledEdgeList) Vertices(fn VertexStep) { 115 | elVertices(el, fn) 116 | } 117 | 118 | func (el LabeledEdgeList) Edges(fn EdgeStep) { 119 | for _, e := range el { 120 | if fn(e) { 121 | return 122 | } 123 | } 124 | } 125 | 126 | // A LabeledArcList is a naive DigraphSource implementation that is backed only by an arc slice. 127 | type LabeledArcList []Arc 128 | 129 | func (el LabeledArcList) Vertices(fn VertexStep) { 130 | elVertices(el, fn) 131 | } 132 | 133 | func (el LabeledArcList) Edges(fn EdgeStep) { 134 | for _, e := range el { 135 | if fn(e) { 136 | return 137 | } 138 | } 139 | } 140 | 141 | func (el LabeledArcList) Arcs(fn ArcStep) { 142 | for _, e := range el { 143 | if fn(e) { 144 | return 145 | } 146 | } 147 | } 148 | 149 | // A DataEdgeList is a naive GraphSource implementation that is backed only by an edge slice. 150 | // 151 | // This variant is for labeled edges. 152 | type DataEdgeList []DataEdge 153 | 154 | func (el DataEdgeList) Vertices(fn VertexStep) { 155 | elVertices(el, fn) 156 | } 157 | 158 | func (el DataEdgeList) Edges(fn EdgeStep) { 159 | for _, e := range el { 160 | if fn(e) { 161 | return 162 | } 163 | } 164 | } 165 | 166 | // A DataArcList is a naive DigraphSource implementation that is backed only by an arc slice. 167 | type DataArcList []Arc 168 | 169 | func (el DataArcList) Vertices(fn VertexStep) { 170 | elVertices(el, fn) 171 | } 172 | 173 | func (el DataArcList) Edges(fn EdgeStep) { 174 | for _, e := range el { 175 | if fn(e) { 176 | return 177 | } 178 | } 179 | } 180 | 181 | func (el DataArcList) Arcs(fn ArcStep) { 182 | for _, e := range el { 183 | if fn(e) { 184 | return 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /edge_test.go: -------------------------------------------------------------------------------- 1 | package gogl_test 2 | 3 | import ( 4 | . "github.com/sdboyer/gocheck" 5 | . "github.com/sdboyer/gogl" 6 | "github.com/sdboyer/gogl/spec" 7 | "gopkg.in/fatih/set.v0" 8 | ) 9 | 10 | type EdgeListSuite struct{} 11 | 12 | var _ = Suite(&EdgeListSuite{}) 13 | 14 | func (s *EdgeListSuite) TestVertices(c *C) { 15 | set1 := set.New(set.NonThreadSafe) 16 | 17 | spec.GraphFixtures["2e3v"].Vertices(func(v Vertex) (terminate bool) { 18 | set1.Add(v) 19 | return 20 | }) 21 | 22 | c.Assert(set1.Size(), Equals, 3) 23 | c.Assert(set1.Has("foo"), Equals, true) 24 | c.Assert(set1.Has("bar"), Equals, true) 25 | c.Assert(set1.Has("baz"), Equals, true) 26 | } 27 | 28 | func (s *EdgeListSuite) TestVerticesTermination(c *C) { 29 | var hit int 30 | spec.GraphFixtures["2e3v"].Vertices(func(v Vertex) (terminate bool) { 31 | hit++ 32 | return true 33 | }) 34 | 35 | c.Assert(hit, Equals, 1) 36 | 37 | spec.GraphFixtures["w-2e3v"].Vertices(func(v Vertex) (terminate bool) { 38 | hit++ 39 | return true 40 | }) 41 | 42 | c.Assert(hit, Equals, 2) 43 | 44 | spec.GraphFixtures["l-2e3v"].Vertices(func(v Vertex) (terminate bool) { 45 | hit++ 46 | return true 47 | }) 48 | 49 | c.Assert(hit, Equals, 3) 50 | 51 | spec.GraphFixtures["d-2e3v"].Vertices(func(v Vertex) (terminate bool) { 52 | hit++ 53 | return true 54 | }) 55 | 56 | c.Assert(hit, Equals, 4) 57 | } 58 | 59 | func (s *EdgeListSuite) TestEdgesTermination(c *C) { 60 | var hit int 61 | spec.GraphFixtures["2e3v"].Edges(func(e Edge) (terminate bool) { 62 | hit++ 63 | return true 64 | }) 65 | c.Assert(hit, Equals, 1) 66 | 67 | spec.GraphFixtures["w-2e3v"].Edges(func(e Edge) (terminate bool) { 68 | hit++ 69 | return true 70 | }) 71 | c.Assert(hit, Equals, 2) 72 | 73 | spec.GraphFixtures["l-2e3v"].Edges(func(e Edge) (terminate bool) { 74 | hit++ 75 | return true 76 | }) 77 | c.Assert(hit, Equals, 3) 78 | 79 | spec.GraphFixtures["d-2e3v"].Edges(func(e Edge) (terminate bool) { 80 | hit++ 81 | return true 82 | }) 83 | c.Assert(hit, Equals, 4) 84 | } 85 | 86 | type ArcListSuite struct{} 87 | 88 | var _ = Suite(&ArcListSuite{}) 89 | 90 | func (s *ArcListSuite) TestVertices(c *C) { 91 | set1 := set.New(set.NonThreadSafe) 92 | 93 | spec.GraphFixtures["2e3v"].Vertices(func(v Vertex) (terminate bool) { 94 | set1.Add(v) 95 | return 96 | }) 97 | 98 | c.Assert(set1.Size(), Equals, 3) 99 | c.Assert(set1.Has("foo"), Equals, true) 100 | c.Assert(set1.Has("bar"), Equals, true) 101 | c.Assert(set1.Has("baz"), Equals, true) 102 | } 103 | 104 | func (s *ArcListSuite) TestVerticesTermination(c *C) { 105 | var hit int 106 | spec.GraphFixtures["2e3v"].Vertices(func(v Vertex) (terminate bool) { 107 | hit++ 108 | return true 109 | }) 110 | 111 | c.Assert(hit, Equals, 1) 112 | 113 | spec.GraphFixtures["w-2e3v"].Vertices(func(v Vertex) (terminate bool) { 114 | hit++ 115 | return true 116 | }) 117 | 118 | c.Assert(hit, Equals, 2) 119 | 120 | spec.GraphFixtures["l-2e3v"].Vertices(func(v Vertex) (terminate bool) { 121 | hit++ 122 | return true 123 | }) 124 | 125 | c.Assert(hit, Equals, 3) 126 | 127 | spec.GraphFixtures["d-2e3v"].Vertices(func(v Vertex) (terminate bool) { 128 | hit++ 129 | return true 130 | }) 131 | 132 | c.Assert(hit, Equals, 4) 133 | } 134 | 135 | func (s *ArcListSuite) TestArcsTermination(c *C) { 136 | var hit int 137 | spec.GraphFixtures["2e3v"].(DigraphSource).Arcs(func(e Arc) (terminate bool) { 138 | hit++ 139 | return true 140 | }) 141 | c.Assert(hit, Equals, 1) 142 | 143 | spec.GraphFixtures["w-2e3v"].(DigraphSource).Arcs(func(e Arc) (terminate bool) { 144 | hit++ 145 | return true 146 | }) 147 | c.Assert(hit, Equals, 2) 148 | 149 | spec.GraphFixtures["l-2e3v"].(DigraphSource).Arcs(func(e Arc) (terminate bool) { 150 | hit++ 151 | return true 152 | }) 153 | c.Assert(hit, Equals, 3) 154 | 155 | spec.GraphFixtures["d-2e3v"].(DigraphSource).Arcs(func(e Arc) (terminate bool) { 156 | hit++ 157 | return true 158 | }) 159 | c.Assert(hit, Equals, 4) 160 | } 161 | 162 | type EdgeSuite struct{} 163 | 164 | var _ = Suite(&EdgeSuite{}) 165 | 166 | func (s *EdgeSuite) TestEdges(c *C) { 167 | e := NewEdge("a", "b") 168 | 169 | a, b := e.Both() 170 | c.Assert(a, Equals, "a") 171 | c.Assert(b, Equals, "b") 172 | 173 | we := NewWeightedEdge("a", "b", 4.2) 174 | 175 | a, b = we.Both() 176 | c.Assert(we.Weight(), Equals, 4.2) 177 | c.Assert(a, Equals, "a") 178 | c.Assert(b, Equals, "b") 179 | 180 | le := NewLabeledEdge("a", "b", "foobar") 181 | 182 | a, b = le.Both() 183 | c.Assert(le.Label(), Equals, "foobar") 184 | c.Assert(a, Equals, "a") 185 | c.Assert(b, Equals, "b") 186 | 187 | de := NewDataEdge("a", "b", NullGraph) 188 | 189 | a, b = de.Both() 190 | c.Assert(de.Data(), Equals, NullGraph) 191 | c.Assert(a, Equals, "a") 192 | c.Assert(b, Equals, "b") 193 | } 194 | 195 | type ArcSuite struct{} 196 | 197 | var _ = Suite(&ArcSuite{}) 198 | 199 | func (s *ArcSuite) TestArcs(c *C) { 200 | e := NewArc("a", "b") 201 | 202 | a, b := e.Both() 203 | c.Assert(e.Source(), Equals, "a") 204 | c.Assert(e.Target(), Equals, "b") 205 | c.Assert(a, Equals, "a") 206 | c.Assert(b, Equals, "b") 207 | 208 | we := NewWeightedArc("a", "b", 4.2) 209 | 210 | a, b = we.Both() 211 | c.Assert(we.Source(), Equals, "a") 212 | c.Assert(we.Target(), Equals, "b") 213 | c.Assert(we.Weight(), Equals, 4.2) 214 | c.Assert(a, Equals, "a") 215 | c.Assert(b, Equals, "b") 216 | 217 | le := NewLabeledArc("a", "b", "foobar") 218 | 219 | a, b = le.Both() 220 | c.Assert(le.Source(), Equals, "a") 221 | c.Assert(le.Target(), Equals, "b") 222 | c.Assert(le.Label(), Equals, "foobar") 223 | c.Assert(a, Equals, "a") 224 | c.Assert(b, Equals, "b") 225 | 226 | de := NewDataArc("a", "b", NullGraph) 227 | 228 | a, b = de.Both() 229 | c.Assert(de.Source(), Equals, "a") 230 | c.Assert(de.Target(), Equals, "b") 231 | c.Assert(de.Data(), Equals, NullGraph) 232 | c.Assert(a, Equals, "a") 233 | c.Assert(b, Equals, "b") 234 | } 235 | -------------------------------------------------------------------------------- /graph/al/al.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | . "github.com/sdboyer/gogl" 5 | "sync" 6 | ) 7 | 8 | /* 9 | Adjacency lists are a relatively simple graph representation. They maintain 10 | a list of vertices, storing information about edge membership relative to 11 | those vertices. This makes vertex-centric operations generally more 12 | efficient, and edge-centric operations generally less efficient, as edges 13 | are represented implicitly. It also makes them inappropriate for more 14 | complex graph types, such as multigraphs. 15 | 16 | gogl's adjacency lists are space-efficient; in a directed graph, the memory 17 | cost for the entire graph G is proportional to V + E; in an undirected graph, 18 | it is V + 2E. 19 | */ 20 | 21 | var alCreators = map[GraphProperties]func() Graph{ 22 | GraphProperties(G_IMMUTABLE | G_DIRECTED | G_BASIC | G_SIMPLE): func() Graph { 23 | return &immutableDirected{al_basic_immut{al_basic{list: make(map[Vertex]map[Vertex]struct{})}}} 24 | }, 25 | GraphProperties(G_MUTABLE | G_DIRECTED | G_BASIC | G_SIMPLE): func() Graph { 26 | return &mutableDirected{al_basic_mut{al_basic{list: make(map[Vertex]map[Vertex]struct{})}, sync.RWMutex{}}} 27 | }, 28 | GraphProperties(G_MUTABLE | G_UNDIRECTED | G_BASIC | G_SIMPLE): func() Graph { 29 | return &mutableUndirected{al_basic_mut{al_basic{list: make(map[Vertex]map[Vertex]struct{})}, sync.RWMutex{}}} 30 | }, 31 | GraphProperties(G_MUTABLE | G_DIRECTED | G_WEIGHTED | G_SIMPLE): func() Graph { 32 | return &weightedDirected{baseWeighted{list: make(map[Vertex]map[Vertex]float64), size: 0, mu: sync.RWMutex{}}} 33 | }, 34 | GraphProperties(G_MUTABLE | G_UNDIRECTED | G_WEIGHTED | G_SIMPLE): func() Graph { 35 | return &weightedUndirected{baseWeighted{list: make(map[Vertex]map[Vertex]float64), size: 0, mu: sync.RWMutex{}}} 36 | }, 37 | GraphProperties(G_MUTABLE | G_DIRECTED | G_LABELED | G_SIMPLE): func() Graph { 38 | return &labeledDirected{baseLabeled{list: make(map[Vertex]map[Vertex]string), size: 0, mu: sync.RWMutex{}}} 39 | }, 40 | GraphProperties(G_MUTABLE | G_UNDIRECTED | G_LABELED | G_SIMPLE): func() Graph { 41 | return &labeledUndirected{baseLabeled{list: make(map[Vertex]map[Vertex]string), size: 0, mu: sync.RWMutex{}}} 42 | }, 43 | GraphProperties(G_MUTABLE | G_DIRECTED | G_DATA | G_SIMPLE): func() Graph { 44 | return &dataDirected{baseData{list: make(map[Vertex]map[Vertex]interface{}), size: 0, mu: sync.RWMutex{}}} 45 | }, 46 | GraphProperties(G_MUTABLE | G_UNDIRECTED | G_DATA | G_SIMPLE): func() Graph { 47 | return &dataUndirected{baseData{list: make(map[Vertex]map[Vertex]interface{}), size: 0, mu: sync.RWMutex{}}} 48 | }, 49 | } 50 | 51 | // Create a graph implementation in the adjacency list style from the provided GraphSpec. 52 | // 53 | // If the GraphSpec contains a GraphSource, it will be imported into the provided graph. 54 | // If the GraphSpec indicates a graph type that is not currently implemented, this function 55 | // will panic. 56 | func G(gs GraphSpec) Graph { 57 | for gp, gf := range alCreators { 58 | 59 | // TODO satisfiability here is not so narrow 60 | if gp&^gs.Props == 0 { 61 | if gs.Source != nil { 62 | if gs.Props&G_DIRECTED == G_DIRECTED { 63 | if dgs, ok := gs.Source.(DigraphSource); ok { 64 | return functorToDirectedAdjacencyList(dgs, gf().(al_digraph)) 65 | } else { 66 | panic("Cannot create a digraph from a graph.") 67 | } 68 | } else { 69 | return functorToAdjacencyList(gs.Source, gf().(al_graph)) 70 | } 71 | } else { 72 | return gf() 73 | } 74 | } 75 | } 76 | 77 | panic("No graph implementation found for spec") 78 | } 79 | 80 | type al_basic struct { 81 | list map[Vertex]map[Vertex]struct{} 82 | size int 83 | } 84 | 85 | // Helper to not have to write struct{} everywhere. 86 | var keyExists = struct{}{} 87 | 88 | // Indicates whether or not the given vertex is present in the graph. 89 | func (g *al_basic) hasVertex(vertex Vertex) (exists bool) { 90 | _, exists = g.list[vertex] 91 | return 92 | } 93 | 94 | // Returns the size (number of edges) in the graph. 95 | func (g *al_basic) Size() int { 96 | return g.size 97 | } 98 | 99 | // Adds the provided vertices to the graph. If a provided vertex is 100 | // already present in the graph, it is a no-op (for that vertex only). 101 | func (g *al_basic) ensureVertex(vertices ...Vertex) { 102 | for _, vertex := range vertices { 103 | if !g.hasVertex(vertex) { 104 | // TODO experiment with different lengths...possibly by analyzing existing density? 105 | g.list[vertex] = make(map[Vertex]struct{}, 10) 106 | } 107 | } 108 | 109 | return 110 | } 111 | 112 | type al_basic_immut struct { 113 | al_basic 114 | } 115 | 116 | // Traverses the graph's vertices in random order, passing each vertex to the 117 | // provided closure. 118 | func (g *al_basic_immut) Vertices(f VertexStep) { 119 | for v := range g.list { 120 | if f(v) { 121 | return 122 | } 123 | } 124 | } 125 | 126 | // Indicates whether or not the given vertex is present in the graph. 127 | func (g *al_basic_immut) HasVertex(vertex Vertex) bool { 128 | return g.hasVertex(vertex) 129 | } 130 | 131 | // Returns the order (number of vertices) in the graph. 132 | func (g *al_basic_immut) Order() int { 133 | return len(g.list) 134 | } 135 | 136 | type al_basic_mut struct { 137 | al_basic 138 | mu sync.RWMutex 139 | } 140 | 141 | /* Base al_basic_mut methods */ 142 | 143 | // Traverses the graph's vertices in random order, passing each vertex to the 144 | // provided closure. 145 | func (g *al_basic_mut) Vertices(f VertexStep) { 146 | g.mu.RLock() 147 | defer g.mu.RUnlock() 148 | 149 | for v := range g.list { 150 | if f(v) { 151 | return 152 | } 153 | } 154 | } 155 | 156 | // Indicates whether or not the given vertex is present in the graph. 157 | func (g *al_basic_mut) HasVertex(vertex Vertex) (exists bool) { 158 | g.mu.RLock() 159 | defer g.mu.RUnlock() 160 | 161 | exists = g.hasVertex(vertex) 162 | return 163 | } 164 | 165 | // Returns the order (number of vertices) in the graph. 166 | func (g *al_basic_mut) Order() int { 167 | g.mu.RLock() 168 | defer g.mu.RUnlock() 169 | 170 | return len(g.list) 171 | } 172 | 173 | // Adds the provided vertices to the graph. If a provided vertex is 174 | // already present in the graph, it is a no-op (for that vertex only). 175 | func (g *al_basic_mut) EnsureVertex(vertices ...Vertex) { 176 | if len(vertices) == 0 { 177 | return 178 | } 179 | 180 | g.mu.Lock() 181 | defer g.mu.Unlock() 182 | 183 | g.ensureVertex(vertices...) 184 | } 185 | -------------------------------------------------------------------------------- /graph/al/al_shared.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | . "github.com/sdboyer/gogl" 5 | ) 6 | 7 | // Contains behaviors shared across adjacency list implementations. 8 | 9 | type al_graph interface { 10 | Graph 11 | ensureVertex(...Vertex) 12 | hasVertex(Vertex) bool 13 | } 14 | 15 | type al_digraph interface { 16 | Digraph 17 | ensureVertex(...Vertex) 18 | hasVertex(Vertex) bool 19 | } 20 | 21 | type al_ea interface { 22 | al_graph 23 | addEdges(...Edge) 24 | } 25 | 26 | type al_dea interface { 27 | al_digraph 28 | addArcs(...Arc) 29 | } 30 | 31 | type al_wea interface { 32 | al_graph 33 | addEdges(...WeightedEdge) 34 | } 35 | 36 | type al_dwea interface { 37 | al_digraph 38 | addArcs(...WeightedArc) 39 | } 40 | 41 | type al_lea interface { 42 | al_graph 43 | addEdges(...LabeledEdge) 44 | } 45 | 46 | type al_dlea interface { 47 | al_digraph 48 | addArcs(...LabeledArc) 49 | } 50 | 51 | type al_pea interface { 52 | al_graph 53 | addEdges(...DataEdge) 54 | } 55 | 56 | type al_dpea interface { 57 | al_digraph 58 | addArcs(...DataArc) 59 | } 60 | 61 | // Copies an incoming graph into any of the implemented adjacency list types. 62 | // 63 | // This encapsulates the full matrix of conversion possibilities between 64 | // different graph edge types, for undirected graphs. 65 | func functorToAdjacencyList(from GraphSource, to al_graph) Graph { 66 | vf := func(from GraphSource, to al_graph) { 67 | if Order(to) != Order(from) { 68 | from.Vertices(func(vertex Vertex) (terminate bool) { 69 | to.ensureVertex(vertex) 70 | return 71 | }) 72 | 73 | } 74 | } 75 | 76 | if g, ok := to.(al_ea); ok { 77 | from.Edges(func(edge Edge) (terminate bool) { 78 | g.addEdges(edge) 79 | return 80 | }) 81 | vf(from, g) 82 | } else if g, ok := to.(al_wea); ok { 83 | from.Edges(func(edge Edge) (terminate bool) { 84 | if e, ok := edge.(WeightedEdge); ok { 85 | g.addEdges(e) 86 | } else { 87 | u, v := edge.Both() 88 | g.addEdges(NewWeightedEdge(u, v, 0)) 89 | } 90 | return 91 | }) 92 | vf(from, g) 93 | } else if g, ok := to.(al_lea); ok { 94 | from.Edges(func(edge Edge) (terminate bool) { 95 | if e, ok := edge.(LabeledEdge); ok { 96 | g.addEdges(e) 97 | } else { 98 | u, v := edge.Both() 99 | g.addEdges(NewLabeledEdge(u, v, "")) 100 | } 101 | return 102 | }) 103 | vf(from, g) 104 | } else if g, ok := to.(al_pea); ok { 105 | from.Edges(func(edge Edge) (terminate bool) { 106 | if e, ok := edge.(DataEdge); ok { 107 | g.addEdges(e) 108 | } else { 109 | u, v := edge.Both() 110 | g.addEdges(NewDataEdge(u, v, nil)) 111 | } 112 | return 113 | }) 114 | vf(from, g) 115 | } else if g, ok := to.(al_ea); ok { 116 | from.Edges(func(edge Edge) (terminate bool) { 117 | g.addEdges(edge) 118 | return 119 | }) 120 | vf(from, g) 121 | } else { 122 | panic("Target graph did not implement a recognized adjacency list internal type") 123 | } 124 | 125 | return to.(Graph) 126 | } 127 | 128 | // Copies an incoming graph into any of the implemented adjacency list types. 129 | // 130 | // This encapsulates the full matrix of conversion possibilities between 131 | // different graph edge types, for directed graphs. 132 | func functorToDirectedAdjacencyList(from DigraphSource, to al_digraph) Digraph { 133 | vf := func(from GraphSource, to al_graph) { 134 | if Order(to) != Order(from) { 135 | from.Vertices(func(vertex Vertex) (terminate bool) { 136 | to.ensureVertex(vertex) 137 | return 138 | }) 139 | 140 | } 141 | } 142 | 143 | if g, ok := to.(al_dea); ok { 144 | from.Arcs(func(arc Arc) (terminate bool) { 145 | g.addArcs(arc) 146 | return 147 | }) 148 | vf(from, g) 149 | } else if g, ok := to.(al_dwea); ok { 150 | from.Arcs(func(arc Arc) (terminate bool) { 151 | if e, ok := arc.(WeightedArc); ok { 152 | g.addArcs(e) 153 | } else { 154 | g.addArcs(NewWeightedArc(arc.Source(), arc.Target(), 0)) 155 | } 156 | return 157 | }) 158 | vf(from, g) 159 | } else if g, ok := to.(al_dlea); ok { 160 | from.Arcs(func(arc Arc) (terminate bool) { 161 | if e, ok := arc.(LabeledArc); ok { 162 | g.addArcs(e) 163 | } else { 164 | g.addArcs(NewLabeledArc(arc.Source(), arc.Target(), "")) 165 | } 166 | return 167 | }) 168 | vf(from, g) 169 | } else if g, ok := to.(al_dpea); ok { 170 | from.Arcs(func(arc Arc) (terminate bool) { 171 | if e, ok := arc.(DataArc); ok { 172 | g.addArcs(e) 173 | } else { 174 | g.addArcs(NewDataArc(arc.Source(), arc.Target(), nil)) 175 | } 176 | return 177 | }) 178 | vf(from, g) 179 | } else { 180 | panic("Target graph did not implement a recognized adjacency list internal type") 181 | } 182 | 183 | return to.(Digraph) 184 | } 185 | 186 | func eachVertexInAdjacencyList(list interface{}, vertex Vertex, vs VertexStep) { 187 | switch l := list.(type) { 188 | case map[Vertex]map[Vertex]struct{}: 189 | if _, exists := l[vertex]; exists { 190 | for adjacent := range l[vertex] { 191 | if vs(adjacent) { 192 | return 193 | } 194 | } 195 | } 196 | case map[Vertex]map[Vertex]float64: 197 | if _, exists := l[vertex]; exists { 198 | for adjacent := range l[vertex] { 199 | if vs(adjacent) { 200 | return 201 | } 202 | } 203 | } 204 | case map[Vertex]map[Vertex]string: 205 | if _, exists := l[vertex]; exists { 206 | for adjacent := range l[vertex] { 207 | if vs(adjacent) { 208 | return 209 | } 210 | } 211 | } 212 | case map[Vertex]map[Vertex]interface{}: 213 | if _, exists := l[vertex]; exists { 214 | for adjacent := range l[vertex] { 215 | if vs(adjacent) { 216 | return 217 | } 218 | } 219 | } 220 | default: 221 | panic("Unrecognized adjacency list map type.") 222 | } 223 | } 224 | 225 | func eachPredecessorOf(list interface{}, vertex Vertex, vs VertexStep) { 226 | switch l := list.(type) { 227 | case map[Vertex]map[Vertex]struct{}: 228 | if _, exists := l[vertex]; exists { 229 | for candidate, adjacent := range l { 230 | for target := range adjacent { 231 | if target == vertex { 232 | if vs(candidate) { 233 | return 234 | } 235 | } 236 | } 237 | } 238 | } 239 | case map[Vertex]map[Vertex]float64: 240 | if _, exists := l[vertex]; exists { 241 | for candidate, adjacent := range l { 242 | for target := range adjacent { 243 | if target == vertex { 244 | if vs(candidate) { 245 | return 246 | } 247 | } 248 | } 249 | } 250 | } 251 | case map[Vertex]map[Vertex]string: 252 | if _, exists := l[vertex]; exists { 253 | for candidate, adjacent := range l { 254 | for target := range adjacent { 255 | if target == vertex { 256 | if vs(candidate) { 257 | return 258 | } 259 | } 260 | } 261 | } 262 | } 263 | case map[Vertex]map[Vertex]interface{}: 264 | if _, exists := l[vertex]; exists { 265 | for candidate, adjacent := range l { 266 | for target := range adjacent { 267 | if target == vertex { 268 | if vs(candidate) { 269 | return 270 | } 271 | } 272 | } 273 | } 274 | } 275 | default: 276 | panic("Unrecognized adjacency list map type.") 277 | } 278 | 279 | } 280 | 281 | func inDegreeOf(g al_digraph, v Vertex) (degree int, exists bool) { 282 | if exists = g.hasVertex(v); exists { 283 | g.Arcs(func(e Arc) (terminate bool) { 284 | if v == e.Target() { 285 | degree++ 286 | } 287 | return 288 | }) 289 | } 290 | return 291 | } 292 | 293 | func eachEdgeIncidentToDirected(g al_digraph, v Vertex, f EdgeStep) { 294 | if !g.hasVertex(v) { 295 | return 296 | } 297 | 298 | var terminate bool 299 | interloper := func(e Arc) bool { 300 | terminate = terminate || f(e) 301 | return terminate 302 | } 303 | 304 | g.ArcsFrom(v, interloper) 305 | g.ArcsTo(v, interloper) 306 | } 307 | -------------------------------------------------------------------------------- /graph/al/al_test.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/sdboyer/gocheck" 7 | "github.com/sdboyer/gogl/spec" 8 | ) 9 | 10 | // Hook gocheck into the go test runner 11 | func TestHookup(t *testing.T) { gocheck.TestingT(t) } 12 | 13 | func init() { 14 | for gp := range alCreators { 15 | spec.SetUpTestsFromSpec(gp, G) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /graph/al/data.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | "sync" 5 | 6 | . "github.com/sdboyer/gogl" 7 | "gopkg.in/fatih/set.v0" 8 | ) 9 | 10 | // This is implemented as an adjacency list, because those are simple. 11 | type baseData struct { 12 | list map[Vertex]map[Vertex]interface{} 13 | size int 14 | mu sync.RWMutex 15 | } 16 | 17 | /* baseData shared methods */ 18 | 19 | // Traverses the graph's vertices in random order, passing each vertex to the 20 | // provided closure. 21 | func (g *baseData) Vertices(f VertexStep) { 22 | g.mu.RLock() 23 | defer g.mu.RUnlock() 24 | 25 | for v := range g.list { 26 | if f(v) { 27 | return 28 | } 29 | } 30 | } 31 | 32 | // Indicates whether or not the given vertex is present in the graph. 33 | func (g *baseData) HasVertex(vertex Vertex) (exists bool) { 34 | g.mu.RLock() 35 | defer g.mu.RUnlock() 36 | 37 | exists = g.hasVertex(vertex) 38 | return 39 | } 40 | 41 | // Indicates whether or not the given vertex is present in the graph. 42 | func (g *baseData) hasVertex(vertex Vertex) (exists bool) { 43 | _, exists = g.list[vertex] 44 | return 45 | } 46 | 47 | // Returns the order (number of vertices) in the graph. 48 | func (g *baseData) Order() int { 49 | g.mu.RLock() 50 | defer g.mu.RUnlock() 51 | 52 | return len(g.list) 53 | } 54 | 55 | // Returns the size (number of edges) in the graph. 56 | func (g *baseData) Size() int { 57 | return g.size 58 | } 59 | 60 | // Adds the provided vertices to the graph. If a provided vertex is 61 | // already present in the graph, it is a no-op (for that vertex only). 62 | func (g *baseData) EnsureVertex(vertices ...Vertex) { 63 | if len(vertices) == 0 { 64 | return 65 | } 66 | 67 | g.mu.Lock() 68 | defer g.mu.Unlock() 69 | 70 | g.ensureVertex(vertices...) 71 | } 72 | 73 | // Adds the provided vertices to the graph. If a provided vertex is 74 | // already present in the graph, it is a no-op (for that vertex only). 75 | func (g *baseData) ensureVertex(vertices ...Vertex) { 76 | for _, vertex := range vertices { 77 | if !g.hasVertex(vertex) { 78 | // TODO experiment with different lengths...possibly by analyzing existing density? 79 | g.list[vertex] = make(map[Vertex]interface{}, 10) 80 | } 81 | } 82 | 83 | return 84 | } 85 | 86 | /* DirectedData implementation */ 87 | 88 | type dataDirected struct { 89 | baseData 90 | } 91 | 92 | // Returns the outdegree of the provided vertex. If the vertex is not present in the 93 | // graph, the second return value will be false. 94 | func (g *dataDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) { 95 | g.mu.RLock() 96 | defer g.mu.RUnlock() 97 | 98 | if exists = g.hasVertex(vertex); exists { 99 | degree = len(g.list[vertex]) 100 | } 101 | return 102 | } 103 | 104 | // Returns the indegree of the provided vertex. If the vertex is not present in the 105 | // graph, the second return value will be false. 106 | // 107 | // Note that getting indegree is inefficient for directed adjacency lists; it requires 108 | // a full scan of the graph's edge set. 109 | func (g *dataDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) { 110 | g.mu.RLock() 111 | defer g.mu.RUnlock() 112 | return inDegreeOf(g, vertex) 113 | } 114 | 115 | // Returns the degree of the provided vertex, counting both in and out-edges. 116 | func (g *dataDirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 117 | g.mu.RLock() 118 | defer g.mu.RUnlock() 119 | 120 | indegree, exists := inDegreeOf(g, vertex) 121 | outdegree, exists := g.OutDegreeOf(vertex) 122 | return indegree + outdegree, exists 123 | } 124 | 125 | // Enumerates the set of all edges incident to the provided vertex. 126 | func (g *dataDirected) IncidentTo(v Vertex, f EdgeStep) { 127 | g.mu.RLock() 128 | defer g.mu.RUnlock() 129 | eachEdgeIncidentToDirected(g, v, f) 130 | } 131 | 132 | // Enumerates the vertices adjacent to the provided vertex. 133 | func (g *dataDirected) AdjacentTo(start Vertex, f VertexStep) { 134 | g.mu.RLock() 135 | defer g.mu.RUnlock() 136 | 137 | g.IncidentTo(start, func(e Edge) bool { 138 | u, v := e.Both() 139 | if u == start { 140 | return f(v) 141 | } else { 142 | return f(u) 143 | } 144 | }) 145 | } 146 | 147 | // Enumerates the set of out-edges for the provided vertex. 148 | func (g *dataDirected) ArcsFrom(v Vertex, f ArcStep) { 149 | g.mu.RLock() 150 | defer g.mu.RUnlock() 151 | 152 | if !g.hasVertex(v) { 153 | return 154 | } 155 | 156 | for adjacent, data := range g.list[v] { 157 | if f(NewDataArc(v, adjacent, data)) { 158 | return 159 | } 160 | } 161 | } 162 | 163 | func (g *dataDirected) SuccessorsOf(v Vertex, f VertexStep) { 164 | g.mu.RLock() 165 | defer g.mu.RUnlock() 166 | 167 | eachVertexInAdjacencyList(g.list, v, f) 168 | } 169 | 170 | // Enumerates the set of in-edges for the provided vertex. 171 | func (g *dataDirected) ArcsTo(v Vertex, f ArcStep) { 172 | g.mu.RLock() 173 | defer g.mu.RUnlock() 174 | 175 | if !g.hasVertex(v) { 176 | return 177 | } 178 | 179 | for candidate, adjacent := range g.list { 180 | for target, data := range adjacent { 181 | if target == v { 182 | if f(NewDataArc(candidate, target, data)) { 183 | return 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | func (g *dataDirected) PredecessorsOf(v Vertex, f VertexStep) { 191 | g.mu.RLock() 192 | defer g.mu.RUnlock() 193 | 194 | eachPredecessorOf(g.list, v, f) 195 | } 196 | 197 | // Traverses the set of edges in the graph, passing each edge to the 198 | // provided closure. 199 | func (g *dataDirected) Edges(f EdgeStep) { 200 | g.mu.RLock() 201 | defer g.mu.RUnlock() 202 | 203 | for source, adjacent := range g.list { 204 | for target, data := range adjacent { 205 | if f(NewDataEdge(source, target, data)) { 206 | return 207 | } 208 | } 209 | } 210 | } 211 | 212 | // Traverses the set of arcs in the graph, passing each arc to the 213 | // provided closure. 214 | func (g *dataDirected) Arcs(f ArcStep) { 215 | g.mu.RLock() 216 | defer g.mu.RUnlock() 217 | 218 | for source, adjacent := range g.list { 219 | for target, data := range adjacent { 220 | if f(NewDataArc(source, target, data)) { 221 | return 222 | } 223 | } 224 | } 225 | } 226 | 227 | // Indicates whether or not the given edge is present in the graph. It matches 228 | // based solely on the presence of an edge, disregarding edge property. 229 | func (g *dataDirected) HasEdge(edge Edge) bool { 230 | g.mu.RLock() 231 | defer g.mu.RUnlock() 232 | 233 | u, v := edge.Both() 234 | _, exists := g.list[u][v] 235 | if !exists { 236 | _, exists = g.list[v][u] 237 | } 238 | return exists 239 | } 240 | 241 | // Indicates whether or not the given arc is present in the graph. 242 | func (g *dataDirected) HasArc(arc Arc) bool { 243 | g.mu.RLock() 244 | defer g.mu.RUnlock() 245 | 246 | _, exists := g.list[arc.Source()][arc.Target()] 247 | return exists 248 | } 249 | 250 | // Indicates whether or not the given property edge is present in the graph. 251 | // It will only match if the provided DataEdge has the same property as 252 | // the edge contained in the graph. 253 | func (g *dataDirected) HasDataEdge(edge DataEdge) bool { 254 | g.mu.RLock() 255 | defer g.mu.RUnlock() 256 | 257 | u, v := edge.Both() 258 | if data, exists := g.list[u][v]; exists { 259 | return data == edge.Data() 260 | } else if data, exists = g.list[v][u]; exists { 261 | return data == edge.Data() 262 | } 263 | return false 264 | } 265 | 266 | // Indicates whether or not the given data arc is present in the graph. 267 | // It will only match if the provided DataEdge has the same data as 268 | // the edge contained in the graph. 269 | func (g *dataDirected) HasDataArc(arc DataArc) bool { 270 | g.mu.RLock() 271 | defer g.mu.RUnlock() 272 | 273 | if data, exists := g.list[arc.Source()][arc.Target()]; exists { 274 | return data == arc.Data() 275 | } 276 | return false 277 | } 278 | 279 | // Returns the density of the graph. Density is the ratio of edge count to the 280 | // number of edges there would be in complete graph (maximum edge count). 281 | func (g *dataDirected) Density() float64 { 282 | g.mu.RLock() 283 | defer g.mu.RUnlock() 284 | 285 | order := g.Order() 286 | return float64(g.Size()) / float64(order*(order-1)) 287 | } 288 | 289 | // Removes a vertex from the graph. Also removes any edges of which that 290 | // vertex is a member. 291 | func (g *dataDirected) RemoveVertex(vertices ...Vertex) { 292 | if len(vertices) == 0 { 293 | return 294 | } 295 | 296 | g.mu.Lock() 297 | defer g.mu.Unlock() 298 | 299 | for _, vertex := range vertices { 300 | if g.hasVertex(vertex) { 301 | g.size -= len(g.list[vertex]) 302 | delete(g.list, vertex) 303 | 304 | for _, adjacent := range g.list { 305 | if _, has := adjacent[vertex]; has { 306 | delete(adjacent, vertex) 307 | g.size-- 308 | } 309 | } 310 | } 311 | } 312 | return 313 | } 314 | 315 | // Adds arcs to the graph. 316 | func (g *dataDirected) AddArcs(arcs ...DataArc) { 317 | if len(arcs) == 0 { 318 | return 319 | } 320 | 321 | g.mu.Lock() 322 | defer g.mu.Unlock() 323 | 324 | g.addArcs(arcs...) 325 | } 326 | 327 | // Adds a new arc to the graph. 328 | func (g *dataDirected) addArcs(arcs ...DataArc) { 329 | for _, arc := range arcs { 330 | u, v := arc.Both() 331 | g.ensureVertex(u, v) 332 | 333 | if _, exists := g.list[u][v]; !exists { 334 | g.list[u][v] = arc.Data() 335 | g.size++ 336 | } 337 | } 338 | } 339 | 340 | // Removes arcs from the graph. This does NOT remove vertex members of the 341 | // removed arcs. 342 | func (g *dataDirected) RemoveArcs(arcs ...DataArc) { 343 | if len(arcs) == 0 { 344 | return 345 | } 346 | 347 | g.mu.Lock() 348 | defer g.mu.Unlock() 349 | 350 | for _, arc := range arcs { 351 | s, t := arc.Both() 352 | if _, exists := g.list[s][t]; exists { 353 | delete(g.list[s], t) 354 | g.size-- 355 | } 356 | } 357 | } 358 | 359 | func (g *dataDirected) Transpose() Digraph { 360 | g.mu.RLock() 361 | defer g.mu.RUnlock() 362 | 363 | g2 := &dataDirected{} 364 | g2.list = make(map[Vertex]map[Vertex]interface{}) 365 | 366 | // Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps 367 | startcap := int(g.Size() / g.Order()) 368 | 369 | for source, adjacent := range g.list { 370 | if !g2.hasVertex(source) { 371 | g2.list[source] = make(map[Vertex]interface{}, startcap+1) 372 | } 373 | 374 | for target, data := range adjacent { 375 | if !g2.hasVertex(target) { 376 | g2.list[target] = make(map[Vertex]interface{}, startcap+1) 377 | } 378 | g2.list[target][source] = data 379 | } 380 | } 381 | 382 | return g2 383 | } 384 | 385 | /* UndirectedData implementation */ 386 | 387 | type dataUndirected struct { 388 | baseData 389 | } 390 | 391 | // Returns the degree of the provided vertex. If the vertex is not present in the 392 | // graph, the second return value will be false. 393 | func (g *dataUndirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 394 | g.mu.RLock() 395 | defer g.mu.RUnlock() 396 | 397 | if exists = g.hasVertex(vertex); exists { 398 | degree = len(g.list[vertex]) 399 | } 400 | return 401 | } 402 | 403 | // Traverses the set of edges in the graph, passing each edge to the 404 | // provided closure. 405 | func (g *dataUndirected) Edges(f EdgeStep) { 406 | g.mu.RLock() 407 | defer g.mu.RUnlock() 408 | 409 | visited := set.New(set.NonThreadSafe) 410 | 411 | var e DataEdge 412 | for source, adjacent := range g.list { 413 | for target, data := range adjacent { 414 | e = NewDataEdge(source, target, data) 415 | if !visited.Has(NewEdge(e.Both())) { 416 | visited.Add(NewEdge(target, source)) 417 | if f(e) { 418 | return 419 | } 420 | } 421 | } 422 | } 423 | } 424 | 425 | // Enumerates the set of all edges incident to the provided vertex. 426 | func (g *dataUndirected) IncidentTo(v Vertex, f EdgeStep) { 427 | g.mu.RLock() 428 | defer g.mu.RUnlock() 429 | 430 | if !g.hasVertex(v) { 431 | return 432 | } 433 | 434 | for adjacent, data := range g.list[v] { 435 | if f(NewDataEdge(v, adjacent, data)) { 436 | return 437 | } 438 | } 439 | } 440 | 441 | // Enumerates the vertices adjacent to the provided vertex. 442 | func (g *dataUndirected) AdjacentTo(vertex Vertex, f VertexStep) { 443 | g.mu.RLock() 444 | defer g.mu.RUnlock() 445 | 446 | eachVertexInAdjacencyList(g.list, vertex, f) 447 | } 448 | 449 | // Indicates whether or not the given edge is present in the graph. It matches 450 | // based solely on the presence of an edge, disregarding edge property. 451 | func (g *dataUndirected) HasEdge(edge Edge) bool { 452 | g.mu.RLock() 453 | defer g.mu.RUnlock() 454 | 455 | // Spread it into two expressions to avoid evaluating the second if possible 456 | u, v := edge.Both() 457 | _, exists := g.list[u][v] 458 | return exists 459 | } 460 | 461 | // Indicates whether or not the given property edge is present in the graph. 462 | // It will only match if the provided DataEdge has the same property as 463 | // the edge contained in the graph. 464 | func (g *dataUndirected) HasDataEdge(edge DataEdge) bool { 465 | g.mu.RLock() 466 | defer g.mu.RUnlock() 467 | 468 | // Spread it into two expressions to avoid evaluating the second if possible 469 | u, v := edge.Both() 470 | if data, exists := g.list[u][v]; exists { 471 | return edge.Data() == data 472 | } else if data, exists := g.list[v][u]; exists { 473 | return edge.Data() == data 474 | } 475 | return false 476 | } 477 | 478 | // Returns the density of the graph. Density is the ratio of edge count to the 479 | // number of edges there would be in complete graph (maximum edge count). 480 | func (g *dataUndirected) Density() float64 { 481 | g.mu.RLock() 482 | defer g.mu.RUnlock() 483 | 484 | order := g.Order() 485 | return 2 * float64(g.Size()) / float64(order*(order-1)) 486 | } 487 | 488 | // Removes a vertex from the graph. Also removes any edges of which that 489 | // vertex is a member. 490 | func (g *dataUndirected) RemoveVertex(vertices ...Vertex) { 491 | if len(vertices) == 0 { 492 | return 493 | } 494 | 495 | g.mu.Lock() 496 | defer g.mu.Unlock() 497 | 498 | for _, vertex := range vertices { 499 | if g.hasVertex(vertex) { 500 | eachVertexInAdjacencyList(g.list, vertex, func(adjacent Vertex) (terminate bool) { 501 | delete(g.list[adjacent], vertex) 502 | return 503 | }) 504 | g.size -= len(g.list[vertex]) 505 | delete(g.list, vertex) 506 | } 507 | } 508 | return 509 | } 510 | 511 | // Adds edges to the graph. 512 | func (g *dataUndirected) AddEdges(edges ...DataEdge) { 513 | if len(edges) == 0 { 514 | return 515 | } 516 | 517 | g.mu.Lock() 518 | defer g.mu.Unlock() 519 | 520 | g.addEdges(edges...) 521 | } 522 | 523 | // Adds a new edge to the graph. 524 | func (g *dataUndirected) addEdges(edges ...DataEdge) { 525 | for _, edge := range edges { 526 | u, v := edge.Both() 527 | g.ensureVertex(u, v) 528 | 529 | if _, exists := g.list[u][v]; !exists { 530 | d := edge.Data() 531 | g.list[u][v] = d 532 | g.list[v][u] = d 533 | g.size++ 534 | } 535 | } 536 | } 537 | 538 | // Removes edges from the graph. This does NOT remove vertex members of the 539 | // removed edges. 540 | func (g *dataUndirected) RemoveEdges(edges ...DataEdge) { 541 | if len(edges) == 0 { 542 | return 543 | } 544 | 545 | g.mu.Lock() 546 | defer g.mu.Unlock() 547 | 548 | for _, edge := range edges { 549 | s, t := edge.Both() 550 | if _, exists := g.list[s][t]; exists { 551 | delete(g.list[s], t) 552 | delete(g.list[t], s) 553 | g.size-- 554 | } 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /graph/al/directed.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | . "github.com/sdboyer/gogl" 5 | ) 6 | 7 | type mutableDirected struct { 8 | al_basic_mut 9 | } 10 | 11 | /* mutableDirected additions */ 12 | 13 | // Returns the outdegree of the provided vertex. If the vertex is not present in the 14 | // graph, the second return value will be false. 15 | func (g *mutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) { 16 | g.mu.RLock() 17 | defer g.mu.RUnlock() 18 | 19 | if exists = g.hasVertex(vertex); exists { 20 | degree = len(g.list[vertex]) 21 | } 22 | return 23 | } 24 | 25 | // Returns the indegree of the provided vertex. If the vertex is not present in the 26 | // graph, the second return value will be false. 27 | // 28 | // Note that getting indegree is inefficient for directed adjacency lists; it requires 29 | // a full scan of the graph's edge set. 30 | func (g *mutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) { 31 | g.mu.RLock() 32 | defer g.mu.RUnlock() 33 | return inDegreeOf(g, vertex) 34 | } 35 | 36 | // Returns the degree of the provided vertex, counting both in and out-edges. 37 | func (g *mutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 38 | g.mu.RLock() 39 | defer g.mu.RUnlock() 40 | 41 | indegree, exists := inDegreeOf(g, vertex) 42 | outdegree, exists := g.OutDegreeOf(vertex) 43 | return indegree + outdegree, exists 44 | } 45 | 46 | // Traverses the set of edges in the graph, passing each edge to the 47 | // provided closure. 48 | func (g *mutableDirected) Edges(f EdgeStep) { 49 | g.mu.RLock() 50 | defer g.mu.RUnlock() 51 | 52 | for source, adjacent := range g.list { 53 | for target := range adjacent { 54 | if f(NewEdge(source, target)) { 55 | return 56 | } 57 | } 58 | } 59 | } 60 | 61 | // Traverses the set of arcs in the graph, passing each arc to the 62 | // provided closure. 63 | func (g *mutableDirected) Arcs(f ArcStep) { 64 | g.mu.RLock() 65 | defer g.mu.RUnlock() 66 | 67 | for source, adjacent := range g.list { 68 | for target := range adjacent { 69 | if f(NewArc(source, target)) { 70 | return 71 | } 72 | } 73 | } 74 | } 75 | 76 | // Enumerates the set of all edges incident to the provided vertex. 77 | func (g *mutableDirected) IncidentTo(v Vertex, f EdgeStep) { 78 | g.mu.RLock() 79 | defer g.mu.RUnlock() 80 | eachEdgeIncidentToDirected(g, v, f) 81 | } 82 | 83 | // Enumerates the vertices adjacent to the provided vertex. 84 | func (g *mutableDirected) AdjacentTo(start Vertex, f VertexStep) { 85 | g.mu.RLock() 86 | defer g.mu.RUnlock() 87 | 88 | g.IncidentTo(start, func(e Edge) bool { 89 | u, v := e.Both() 90 | if u == start { 91 | return f(v) 92 | } else { 93 | return f(u) 94 | } 95 | }) 96 | } 97 | 98 | // Enumerates the set of out-edges for the provided vertex. 99 | func (g *mutableDirected) ArcsFrom(v Vertex, f ArcStep) { 100 | g.mu.RLock() 101 | defer g.mu.RUnlock() 102 | 103 | if !g.hasVertex(v) { 104 | return 105 | } 106 | 107 | for adjacent := range g.list[v] { 108 | if f(NewArc(v, adjacent)) { 109 | return 110 | } 111 | } 112 | } 113 | 114 | func (g *mutableDirected) SuccessorsOf(v Vertex, f VertexStep) { 115 | g.mu.RLock() 116 | defer g.mu.RUnlock() 117 | 118 | eachVertexInAdjacencyList(g.list, v, f) 119 | } 120 | 121 | // Enumerates the set of in-edges for the provided vertex. 122 | func (g *mutableDirected) ArcsTo(v Vertex, f ArcStep) { 123 | g.mu.RLock() 124 | defer g.mu.RUnlock() 125 | 126 | if !g.hasVertex(v) { 127 | return 128 | } 129 | 130 | for candidate, adjacent := range g.list { 131 | for target := range adjacent { 132 | if target == v { 133 | if f(NewArc(candidate, target)) { 134 | return 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | func (g *mutableDirected) PredecessorsOf(v Vertex, f VertexStep) { 142 | g.mu.RLock() 143 | defer g.mu.RUnlock() 144 | 145 | eachPredecessorOf(g.list, v, f) 146 | } 147 | 148 | // Indicates whether or not the given edge is present in the graph. 149 | func (g *mutableDirected) HasEdge(edge Edge) bool { 150 | g.mu.RLock() 151 | defer g.mu.RUnlock() 152 | 153 | u, v := edge.Both() 154 | _, exists := g.list[u][v] 155 | if !exists { 156 | _, exists = g.list[v][u] 157 | } 158 | return exists 159 | } 160 | 161 | // Indicates whether or not the given arc is present in the graph. 162 | func (g *mutableDirected) HasArc(arc Arc) bool { 163 | g.mu.RLock() 164 | defer g.mu.RUnlock() 165 | 166 | _, exists := g.list[arc.Source()][arc.Target()] 167 | return exists 168 | } 169 | 170 | // Returns the density of the graph. Density is the ratio of edge count to the 171 | // number of edges there would be in complete graph (maximum edge count). 172 | func (g *mutableDirected) Density() float64 { 173 | g.mu.RLock() 174 | defer g.mu.RUnlock() 175 | 176 | order := g.Order() 177 | return float64(g.Size()) / float64(order*(order-1)) 178 | } 179 | 180 | // Removes a vertex from the graph. Also removes any edges of which that 181 | // vertex is a member. 182 | func (g *mutableDirected) RemoveVertex(vertices ...Vertex) { 183 | if len(vertices) == 0 { 184 | return 185 | } 186 | 187 | g.mu.Lock() 188 | defer g.mu.Unlock() 189 | 190 | for _, vertex := range vertices { 191 | if g.hasVertex(vertex) { 192 | // TODO Is the expensive search good to do here and now... 193 | // while read-locked? 194 | g.size -= len(g.list[vertex]) 195 | delete(g.list, vertex) 196 | 197 | // TODO consider chunking the list and parallelizing into goroutines 198 | for _, adjacent := range g.list { 199 | if _, has := adjacent[vertex]; has { 200 | delete(adjacent, vertex) 201 | g.size-- 202 | } 203 | } 204 | } 205 | } 206 | return 207 | } 208 | 209 | // Adds arcs to the graph. 210 | func (g *mutableDirected) AddArcs(arcs ...Arc) { 211 | if len(arcs) == 0 { 212 | return 213 | } 214 | 215 | g.mu.Lock() 216 | defer g.mu.Unlock() 217 | 218 | g.addArcs(arcs...) 219 | } 220 | 221 | // Adds a new arc to the graph. 222 | func (g *mutableDirected) addArcs(arcs ...Arc) { 223 | for _, arc := range arcs { 224 | g.ensureVertex(arc.Source(), arc.Target()) 225 | 226 | if _, exists := g.list[arc.Source()][arc.Target()]; !exists { 227 | g.list[arc.Source()][arc.Target()] = keyExists 228 | g.size++ 229 | } 230 | } 231 | } 232 | 233 | // Removes arcs from the graph. This does NOT remove vertex members of the 234 | // removed arcs. 235 | func (g *mutableDirected) RemoveArcs(arcs ...Arc) { 236 | if len(arcs) == 0 { 237 | return 238 | } 239 | 240 | g.mu.Lock() 241 | defer g.mu.Unlock() 242 | 243 | for _, arc := range arcs { 244 | s, t := arc.Both() 245 | if _, exists := g.list[s][t]; exists { 246 | delete(g.list[s], t) 247 | g.size-- 248 | } 249 | } 250 | } 251 | 252 | // Returns a graph with the same vertex and edge set, but with the 253 | // directionality of all its edges reversed. 254 | // 255 | // This implementation returns a new graph object (doubling memory use), 256 | // but not all implementations do so. 257 | func (g *mutableDirected) Transpose() Digraph { 258 | g.mu.RLock() 259 | defer g.mu.RUnlock() 260 | 261 | g2 := &mutableDirected{} 262 | g2.list = make(map[Vertex]map[Vertex]struct{}) 263 | 264 | // Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps 265 | startcap := int(g.Size() / g.Order()) 266 | 267 | for source, adjacent := range g.list { 268 | if !g2.hasVertex(source) { 269 | g2.list[source] = make(map[Vertex]struct{}, startcap+1) 270 | } 271 | for target := range adjacent { 272 | if !g2.hasVertex(target) { 273 | g2.list[target] = make(map[Vertex]struct{}, startcap+1) 274 | } 275 | g2.list[target][source] = keyExists 276 | } 277 | } 278 | 279 | return g2 280 | } 281 | 282 | /* immutableDirected implementation */ 283 | 284 | type immutableDirected struct { 285 | al_basic_immut 286 | } 287 | 288 | // Traverses the set of edges in the graph, passing each edge to the 289 | // provided closure. 290 | func (g *immutableDirected) Edges(f EdgeStep) { 291 | for source, adjacent := range g.list { 292 | for target := range adjacent { 293 | if f(NewEdge(source, target)) { 294 | return 295 | } 296 | } 297 | } 298 | } 299 | 300 | // Traverses the set of arcs in the graph, passing each arc to the 301 | // provided closure. 302 | func (g *immutableDirected) Arcs(f ArcStep) { 303 | for source, adjacent := range g.list { 304 | for target := range adjacent { 305 | if f(NewArc(source, target)) { 306 | return 307 | } 308 | } 309 | } 310 | } 311 | 312 | // Enumerates the set of all edges incident to the provided vertex. 313 | func (g *immutableDirected) IncidentTo(v Vertex, f EdgeStep) { 314 | eachEdgeIncidentToDirected(g, v, f) 315 | } 316 | 317 | // Enumerates the vertices adjacent to the provided vertex. 318 | func (g *immutableDirected) AdjacentTo(start Vertex, f VertexStep) { 319 | g.IncidentTo(start, func(e Edge) bool { 320 | u, v := e.Both() 321 | if u == start { 322 | return f(v) 323 | } else { 324 | return f(u) 325 | } 326 | }) 327 | } 328 | 329 | // Enumerates the set of out-edges for the provided vertex. 330 | func (g *immutableDirected) ArcsFrom(v Vertex, f ArcStep) { 331 | if !g.hasVertex(v) { 332 | return 333 | } 334 | 335 | for adjacent := range g.list[v] { 336 | if f(NewArc(v, adjacent)) { 337 | return 338 | } 339 | } 340 | } 341 | 342 | func (g *immutableDirected) SuccessorsOf(v Vertex, f VertexStep) { 343 | eachVertexInAdjacencyList(g.list, v, f) 344 | } 345 | 346 | // Enumerates the set of in-edges for the provided vertex. 347 | func (g *immutableDirected) ArcsTo(v Vertex, f ArcStep) { 348 | if !g.hasVertex(v) { 349 | return 350 | } 351 | 352 | for candidate, adjacent := range g.list { 353 | for target := range adjacent { 354 | if target == v { 355 | if f(NewArc(candidate, target)) { 356 | return 357 | } 358 | } 359 | } 360 | } 361 | } 362 | 363 | func (g *immutableDirected) PredecessorsOf(v Vertex, f VertexStep) { 364 | eachPredecessorOf(g.list, v, f) 365 | } 366 | 367 | // Returns the density of the graph. Density is the ratio of edge count to the 368 | // number of edges there would be in complete graph (maximum edge count). 369 | func (g *immutableDirected) Density() float64 { 370 | order := g.Order() 371 | return float64(g.Size()) / float64(order*(order-1)) 372 | } 373 | 374 | // Indicates whether or not the given edge is present in the graph. 375 | func (g *immutableDirected) HasEdge(edge Edge) bool { 376 | u, v := edge.Both() 377 | _, exists := g.list[u][v] 378 | if !exists { 379 | _, exists = g.list[v][u] 380 | } 381 | return exists 382 | } 383 | 384 | // Indicates whether or not the given arc is present in the graph. 385 | func (g *immutableDirected) HasArc(arc Arc) bool { 386 | _, exists := g.list[arc.Source()][arc.Target()] 387 | return exists 388 | } 389 | 390 | // Returns the outdegree of the provided vertex. If the vertex is not present in the 391 | // graph, the second return value will be false. 392 | func (g *immutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) { 393 | if exists = g.hasVertex(vertex); exists { 394 | degree = len(g.list[vertex]) 395 | } 396 | return 397 | } 398 | 399 | // Returns the indegree of the provided vertex. If the vertex is not present in the 400 | // graph, the second return value will be false. 401 | // 402 | // Note that getting indegree is inefficient for directed adjacency lists; it requires 403 | // a full scan of the graph's edge set. 404 | func (g *immutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) { 405 | if exists = g.hasVertex(vertex); exists { 406 | g.Arcs(func(e Arc) (terminate bool) { 407 | if vertex == e.Target() { 408 | degree++ 409 | } 410 | return 411 | }) 412 | } 413 | 414 | return 415 | } 416 | 417 | // Returns the degree of the vertex, counting both in and out-edges. 418 | func (g *immutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 419 | indegree, exists := g.InDegreeOf(vertex) 420 | outdegree, exists := g.OutDegreeOf(vertex) 421 | return indegree + outdegree, exists 422 | } 423 | 424 | // Returns a graph with the same vertex and edge set, but with the 425 | // directionality of all its edges reversed. 426 | // 427 | // This implementation returns a new graph object (doubling memory use), 428 | // but not all implementations do so. 429 | func (g *immutableDirected) Transpose() Digraph { 430 | g2 := &immutableDirected{} 431 | g2.list = make(map[Vertex]map[Vertex]struct{}) 432 | 433 | // Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps 434 | startcap := int(g.Size() / g.Order()) 435 | 436 | for source, adjacent := range g.list { 437 | if !g2.hasVertex(source) { 438 | g2.list[source] = make(map[Vertex]struct{}, startcap+1) 439 | } 440 | for target := range adjacent { 441 | if !g2.hasVertex(target) { 442 | g2.list[target] = make(map[Vertex]struct{}, startcap+1) 443 | } 444 | g2.list[target][source] = keyExists 445 | } 446 | } 447 | 448 | return g2 449 | } 450 | 451 | // Adds a new arc to the graph. 452 | func (g *immutableDirected) addArcs(arcs ...Arc) { 453 | for _, arc := range arcs { 454 | g.ensureVertex(arc.Source(), arc.Target()) 455 | 456 | if _, exists := g.list[arc.Source()][arc.Target()]; !exists { 457 | g.list[arc.Source()][arc.Target()] = keyExists 458 | g.size++ 459 | } 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /graph/al/graph_bench_test.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | "time" 7 | 8 | . "github.com/sdboyer/gocheck" 9 | . "github.com/sdboyer/gogl" 10 | ) 11 | 12 | // TODO reimplement with specs 13 | //func SetUpBenchmarksFromBuilder(b GraphBuilder) bool { 14 | //Suite(&GraphBenchSuite{b: b}) 15 | 16 | //return true 17 | //} 18 | 19 | //var _ = SetUpBenchmarksFromBuilder(BMBD) 20 | 21 | type GraphBenchSuite struct { 22 | //b GraphBuilder 23 | g10 Graph 24 | g100 Graph 25 | g1000 Graph 26 | g10000 Graph 27 | g100000 Graph 28 | } 29 | 30 | // An edge type specifically for benchmarking that encompasses all edge types. 31 | type benchEdge struct { 32 | U Vertex 33 | V Vertex 34 | W float64 35 | L string 36 | P interface{} 37 | } 38 | 39 | func (e benchEdge) Source() Vertex { 40 | return e.U 41 | } 42 | 43 | func (e benchEdge) Target() Vertex { 44 | return e.V 45 | } 46 | 47 | func (e benchEdge) Both() (Vertex, Vertex) { 48 | return e.U, e.V 49 | } 50 | 51 | func (e benchEdge) Weight() float64 { 52 | return e.W 53 | } 54 | 55 | func (e benchEdge) Label() string { 56 | return e.L 57 | } 58 | 59 | func (e benchEdge) Data() interface{} { 60 | return e.P 61 | } 62 | 63 | func bernoulliDistributionGenerator(vertexCount uint, edgeProbability int, src rand.Source) GraphSource { 64 | if edgeProbability > 100 || edgeProbability < 1 { 65 | panic("Must designate an edge probability between 1 and 100") 66 | } 67 | 68 | if src == nil { 69 | src = rand.NewSource(time.Now().UnixNano()) 70 | } 71 | 72 | r := rand.New(src) 73 | 74 | list := make([][]benchEdge, vertexCount, vertexCount) 75 | 76 | size := 0 77 | vc := int(vertexCount) 78 | for u := 0; u < vc; u++ { 79 | list[u] = make([]benchEdge, vertexCount, vertexCount) 80 | for v := 0; v < vc; v++ { 81 | // without this conditional, this loop would create a complete graph 82 | if v != u && // no loops 83 | r.Intn(100) <= edgeProbability { // create edge iff probability says so 84 | list[u][v] = benchEdge{U: u, V: v} 85 | size++ 86 | } 87 | } 88 | } 89 | 90 | return &benchGraph{targetOrder: vertexCount, directed: true, list: list, size: size} 91 | } 92 | 93 | // A type of graph intended to serve as a controlled source of graph data for benchmarking. 94 | type benchGraph struct { 95 | targetOrder uint 96 | targetDensity float64 97 | maxDegree uint 98 | minDegree uint 99 | directed bool 100 | list [][]benchEdge 101 | size int 102 | } 103 | 104 | func (g *benchGraph) Vertices(f VertexStep) { 105 | for v := range g.list { 106 | if f(v) { 107 | return 108 | } 109 | } 110 | } 111 | 112 | func (g *benchGraph) Edges(f EdgeStep) { 113 | for _, adj := range g.list { 114 | for _, e := range adj { 115 | if f(e) { 116 | return 117 | } 118 | } 119 | } 120 | } 121 | 122 | func (g *benchGraph) Size() int { 123 | return g.size 124 | } 125 | 126 | func (g *benchGraph) Order() int { 127 | return len(g.list) 128 | } 129 | 130 | // back to reality 131 | 132 | func (s *GraphBenchSuite) SetUpSuite(c *C) { 133 | //src := rand.NewSource(time.Now().UnixNano()) 134 | //s.g10 = s.b.Using(bernoulliDistributionGenerator(10, 50, src)).Graph() 135 | //s.g100 = s.b.Using(bernoulliDistributionGenerator(100, 50, src)).Graph() 136 | //s.g1000 = s.b.Using(bernoulliDistributionGenerator(1000, 50, src)).Graph() 137 | //s.g10000 = s.b.Using(bernoulliDistributionGenerator(10000, 50, src)).Graph() 138 | // s.g100000 = s.b.Using(bernoulliDistributionGenerator(100000, 50, src)).Graph() 139 | } 140 | 141 | func (s *GraphBenchSuite) BenchmarkHasVertex10(c *C) { 142 | benchHasVertex(s.g10, c) 143 | } 144 | 145 | func (s *GraphBenchSuite) BenchmarkHasVertex100(c *C) { 146 | benchHasVertex(s.g100, c) 147 | } 148 | 149 | func (s *GraphBenchSuite) BenchmarkHasVertex1000(c *C) { 150 | benchHasVertex(s.g1000, c) 151 | } 152 | 153 | //func (s *GraphBenchSuite) BenchmarkHasVertex10000(c *C) { 154 | //benchHasVertex(s.g10000, c) 155 | //} 156 | 157 | //func (s *GraphBenchSuite) BenchmarkHasVertex100000(c *C) { 158 | //benchHasVertex(s.g100000, c) 159 | //} 160 | 161 | func benchHasVertex(g Graph, c *C) { 162 | for i := 0; i < c.N; i++ { 163 | g.HasVertex(50) 164 | } 165 | } 166 | 167 | var bgraph = Spec().Using(bernoulliDistributionGenerator(1000, 50, nil)).Create(G) 168 | 169 | func BenchmarkHasVertex(b *testing.B) { 170 | for i := 0; i < b.N; i++ { 171 | bgraph.HasVertex(50) 172 | } 173 | } 174 | 175 | func BenchmarkVertices(b *testing.B) { 176 | for i := 0; i < b.N; i++ { 177 | bgraph.Vertices(func(v Vertex) (terminate bool) { 178 | return 179 | }) 180 | 181 | } 182 | } 183 | 184 | func BenchmarkEdges(b *testing.B) { 185 | for i := 0; i < b.N; i++ { 186 | bgraph.Edges(func(e Edge) (terminate bool) { 187 | return 188 | }) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /graph/al/labeled.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | "sync" 5 | 6 | . "github.com/sdboyer/gogl" 7 | "gopkg.in/fatih/set.v0" 8 | ) 9 | 10 | // This is implemented as an adjacency list, because those are simple. 11 | type baseLabeled struct { 12 | list map[Vertex]map[Vertex]string 13 | size int 14 | mu sync.RWMutex 15 | } 16 | 17 | /* baseLabeled shared methods */ 18 | 19 | // Traverses the graph's vertices in random order, passing each vertex to the 20 | // provided closure. 21 | func (g *baseLabeled) Vertices(f VertexStep) { 22 | g.mu.RLock() 23 | defer g.mu.RUnlock() 24 | 25 | for v := range g.list { 26 | if f(v) { 27 | return 28 | } 29 | } 30 | } 31 | 32 | // Indicates whether or not the given vertex is present in the graph. 33 | func (g *baseLabeled) HasVertex(vertex Vertex) (exists bool) { 34 | g.mu.RLock() 35 | defer g.mu.RUnlock() 36 | 37 | exists = g.hasVertex(vertex) 38 | return 39 | } 40 | 41 | // Indicates whether or not the given vertex is present in the graph. 42 | func (g *baseLabeled) hasVertex(vertex Vertex) (exists bool) { 43 | _, exists = g.list[vertex] 44 | return 45 | } 46 | 47 | // Returns the order (number of vertices) in the graph. 48 | func (g *baseLabeled) Order() int { 49 | g.mu.RLock() 50 | defer g.mu.RUnlock() 51 | 52 | return len(g.list) 53 | } 54 | 55 | // Returns the size (number of edges) in the graph. 56 | func (g *baseLabeled) Size() int { 57 | return g.size 58 | } 59 | 60 | // Adds the provided vertices to the graph. If a provided vertex is 61 | // already present in the graph, it is a no-op (for that vertex only). 62 | func (g *baseLabeled) EnsureVertex(vertices ...Vertex) { 63 | if len(vertices) == 0 { 64 | return 65 | } 66 | 67 | g.mu.Lock() 68 | defer g.mu.Unlock() 69 | 70 | g.ensureVertex(vertices...) 71 | } 72 | 73 | // Adds the provided vertices to the graph. If a provided vertex is 74 | // already present in the graph, it is a no-op (for that vertex only). 75 | func (g *baseLabeled) ensureVertex(vertices ...Vertex) { 76 | for _, vertex := range vertices { 77 | if !g.hasVertex(vertex) { 78 | // TODO experiment with different lengths...possibly by analyzing existing density? 79 | g.list[vertex] = make(map[Vertex]string, 10) 80 | } 81 | } 82 | 83 | return 84 | } 85 | 86 | /* DirectedLabeled implementation */ 87 | 88 | type labeledDirected struct { 89 | baseLabeled 90 | } 91 | 92 | // Returns the outdegree of the provided vertex. If the vertex is not present in the 93 | // graph, the second return value will be false. 94 | func (g *labeledDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) { 95 | g.mu.RLock() 96 | defer g.mu.RUnlock() 97 | 98 | if exists = g.hasVertex(vertex); exists { 99 | degree = len(g.list[vertex]) 100 | } 101 | return 102 | } 103 | 104 | // Returns the indegree of the provided vertex. If the vertex is not present in the 105 | // graph, the second return value will be false. 106 | // 107 | // Note that getting indegree is inefficient for directed adjacency lists; it requires 108 | // a full scan of the graph's edge set. 109 | func (g *labeledDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) { 110 | g.mu.RLock() 111 | defer g.mu.RUnlock() 112 | return inDegreeOf(g, vertex) 113 | } 114 | 115 | // Returns the degree of the provided vertex, counting both in and out-edges. 116 | func (g *labeledDirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 117 | g.mu.RLock() 118 | defer g.mu.RUnlock() 119 | 120 | indegree, exists := inDegreeOf(g, vertex) 121 | outdegree, exists := g.OutDegreeOf(vertex) 122 | return indegree + outdegree, exists 123 | } 124 | 125 | // Traverses the set of edges in the graph, passing each edge to the 126 | // provided closure. 127 | func (g *labeledDirected) Edges(f EdgeStep) { 128 | g.mu.RLock() 129 | defer g.mu.RUnlock() 130 | 131 | for source, adjacent := range g.list { 132 | for target, label := range adjacent { 133 | if f(NewLabeledEdge(source, target, label)) { 134 | return 135 | } 136 | } 137 | } 138 | } 139 | 140 | // Traverses the set of arcs in the graph, passing each arc to the 141 | // provided closure. 142 | func (g *labeledDirected) Arcs(f ArcStep) { 143 | g.mu.RLock() 144 | defer g.mu.RUnlock() 145 | 146 | for source, adjacent := range g.list { 147 | for target, label := range adjacent { 148 | if f(NewLabeledArc(source, target, label)) { 149 | return 150 | } 151 | } 152 | } 153 | } 154 | 155 | // Enumerates the set of all edges incident to the provided vertex. 156 | func (g *labeledDirected) IncidentTo(v Vertex, f EdgeStep) { 157 | g.mu.RLock() 158 | defer g.mu.RUnlock() 159 | eachEdgeIncidentToDirected(g, v, f) 160 | } 161 | 162 | // Enumerates the vertices adjacent to the provided vertex. 163 | func (g *labeledDirected) AdjacentTo(start Vertex, f VertexStep) { 164 | g.mu.RLock() 165 | defer g.mu.RUnlock() 166 | 167 | g.IncidentTo(start, func(e Edge) bool { 168 | u, v := e.Both() 169 | if u == start { 170 | return f(v) 171 | } else { 172 | return f(u) 173 | } 174 | }) 175 | } 176 | 177 | // Enumerates the set of out-edges for the provided vertex. 178 | func (g *labeledDirected) ArcsFrom(v Vertex, f ArcStep) { 179 | g.mu.RLock() 180 | defer g.mu.RUnlock() 181 | 182 | if !g.hasVertex(v) { 183 | return 184 | } 185 | 186 | for adjacent, label := range g.list[v] { 187 | if f(NewLabeledArc(v, adjacent, label)) { 188 | return 189 | } 190 | } 191 | } 192 | 193 | func (g *labeledDirected) SuccessorsOf(v Vertex, f VertexStep) { 194 | g.mu.RLock() 195 | defer g.mu.RUnlock() 196 | 197 | eachVertexInAdjacencyList(g.list, v, f) 198 | } 199 | 200 | // Enumerates the set of in-edges for the provided vertex. 201 | func (g *labeledDirected) ArcsTo(v Vertex, f ArcStep) { 202 | g.mu.RLock() 203 | defer g.mu.RUnlock() 204 | 205 | if !g.hasVertex(v) { 206 | return 207 | } 208 | 209 | for candidate, adjacent := range g.list { 210 | for target, label := range adjacent { 211 | if target == v { 212 | if f(NewLabeledArc(candidate, target, label)) { 213 | return 214 | } 215 | } 216 | } 217 | } 218 | } 219 | 220 | func (g *labeledDirected) PredecessorsOf(v Vertex, f VertexStep) { 221 | g.mu.RLock() 222 | defer g.mu.RUnlock() 223 | 224 | eachPredecessorOf(g.list, v, f) 225 | } 226 | 227 | // Indicates whether or not the given edge is present in the graph. It matches 228 | // based solely on the presence of an edge, disregarding edge label. 229 | func (g *labeledDirected) HasEdge(edge Edge) bool { 230 | g.mu.RLock() 231 | defer g.mu.RUnlock() 232 | 233 | u, v := edge.Both() 234 | _, exists := g.list[u][v] 235 | if !exists { 236 | _, exists = g.list[v][u] 237 | } 238 | return exists 239 | } 240 | 241 | // Indicates whether or not the given arc is present in the graph. 242 | func (g *labeledDirected) HasArc(arc Arc) bool { 243 | g.mu.RLock() 244 | defer g.mu.RUnlock() 245 | 246 | _, exists := g.list[arc.Source()][arc.Target()] 247 | return exists 248 | } 249 | 250 | // Indicates whether or not the given labeled edge is present in the graph. 251 | // It will only match if the provided LabeledEdge has the same label as 252 | // the edge contained in the graph. 253 | func (g *labeledDirected) HasLabeledEdge(edge LabeledEdge) bool { 254 | g.mu.RLock() 255 | defer g.mu.RUnlock() 256 | 257 | u, v := edge.Both() 258 | if label, exists := g.list[u][v]; exists { 259 | return label == edge.Label() 260 | } else if label, exists = g.list[v][u]; exists { 261 | return label == edge.Label() 262 | } 263 | return false 264 | } 265 | 266 | // Indicates whether or not the given labeled arc is present in the graph. 267 | // It will only match if the provided LabeledEdge has the same label as 268 | // the edge contained in the graph. 269 | func (g *labeledDirected) HasLabeledArc(arc LabeledArc) bool { 270 | g.mu.RLock() 271 | defer g.mu.RUnlock() 272 | 273 | if label, exists := g.list[arc.Source()][arc.Target()]; exists { 274 | return label == arc.Label() 275 | } 276 | return false 277 | } 278 | 279 | // Returns the density of the graph. Density is the ratio of edge count to the 280 | // number of edges there would be in complete graph (maximum edge count). 281 | func (g *labeledDirected) Density() float64 { 282 | g.mu.RLock() 283 | defer g.mu.RUnlock() 284 | 285 | order := g.Order() 286 | return float64(g.Size()) / float64(order*(order-1)) 287 | } 288 | 289 | // Removes a vertex from the graph. Also removes any edges of which that 290 | // vertex is a member. 291 | func (g *labeledDirected) RemoveVertex(vertices ...Vertex) { 292 | if len(vertices) == 0 { 293 | return 294 | } 295 | 296 | g.mu.Lock() 297 | defer g.mu.Unlock() 298 | 299 | for _, vertex := range vertices { 300 | if g.hasVertex(vertex) { 301 | g.size -= len(g.list[vertex]) 302 | delete(g.list, vertex) 303 | 304 | for _, adjacent := range g.list { 305 | if _, has := adjacent[vertex]; has { 306 | delete(adjacent, vertex) 307 | g.size-- 308 | } 309 | } 310 | } 311 | } 312 | return 313 | } 314 | 315 | // Adds arcs to the graph. 316 | func (g *labeledDirected) AddArcs(arcs ...LabeledArc) { 317 | if len(arcs) == 0 { 318 | return 319 | } 320 | 321 | g.mu.Lock() 322 | defer g.mu.Unlock() 323 | 324 | g.addArcs(arcs...) 325 | } 326 | 327 | // Adds a new arc to the graph. 328 | func (g *labeledDirected) addArcs(arcs ...LabeledArc) { 329 | for _, arc := range arcs { 330 | g.ensureVertex(arc.Source(), arc.Target()) 331 | 332 | if _, exists := g.list[arc.Source()][arc.Target()]; !exists { 333 | g.list[arc.Source()][arc.Target()] = arc.Label() 334 | g.size++ 335 | } 336 | } 337 | } 338 | 339 | // Removes arcs from the graph. This does NOT remove vertex members of the 340 | // removed arcs. 341 | func (g *labeledDirected) RemoveArcs(arcs ...LabeledArc) { 342 | if len(arcs) == 0 { 343 | return 344 | } 345 | 346 | g.mu.Lock() 347 | defer g.mu.Unlock() 348 | 349 | for _, arc := range arcs { 350 | s, t := arc.Both() 351 | if _, exists := g.list[s][t]; exists { 352 | delete(g.list[s], t) 353 | g.size-- 354 | } 355 | } 356 | } 357 | 358 | func (g *labeledDirected) Transpose() Digraph { 359 | g.mu.RLock() 360 | defer g.mu.RUnlock() 361 | 362 | g2 := &labeledDirected{} 363 | g2.list = make(map[Vertex]map[Vertex]string) 364 | 365 | // Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps 366 | startcap := int(g.Size() / g.Order()) 367 | 368 | for source, adjacent := range g.list { 369 | if !g2.hasVertex(source) { 370 | g2.list[source] = make(map[Vertex]string, startcap+1) 371 | } 372 | 373 | for target, label := range adjacent { 374 | if !g2.hasVertex(target) { 375 | g2.list[target] = make(map[Vertex]string, startcap+1) 376 | } 377 | g2.list[target][source] = label 378 | } 379 | } 380 | 381 | return g2 382 | } 383 | 384 | /* UndirectedLabeled implementation */ 385 | 386 | type labeledUndirected struct { 387 | baseLabeled 388 | } 389 | 390 | // Returns the degree of the provided vertex. If the vertex is not present in the 391 | // graph, the second return value will be false. 392 | func (g *labeledUndirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 393 | g.mu.RLock() 394 | defer g.mu.RUnlock() 395 | 396 | if exists = g.hasVertex(vertex); exists { 397 | degree = len(g.list[vertex]) 398 | } 399 | return 400 | } 401 | 402 | // Traverses the set of edges in the graph, passing each edge to the 403 | // provided closure. 404 | func (g *labeledUndirected) Edges(f EdgeStep) { 405 | g.mu.RLock() 406 | defer g.mu.RUnlock() 407 | 408 | visited := set.New(set.NonThreadSafe) 409 | 410 | var e LabeledEdge 411 | for source, adjacent := range g.list { 412 | for target, label := range adjacent { 413 | e = NewLabeledEdge(source, target, label) 414 | if !visited.Has(NewEdge(e.Both())) { 415 | visited.Add(NewEdge(target, source)) 416 | if f(e) { 417 | return 418 | } 419 | } 420 | } 421 | } 422 | } 423 | 424 | // Enumerates the set of all edges incident to the provided vertex. 425 | func (g *labeledUndirected) IncidentTo(v Vertex, f EdgeStep) { 426 | g.mu.RLock() 427 | defer g.mu.RUnlock() 428 | 429 | if !g.hasVertex(v) { 430 | return 431 | } 432 | 433 | for adjacent, label := range g.list[v] { 434 | if f(NewLabeledEdge(v, adjacent, label)) { 435 | return 436 | } 437 | } 438 | } 439 | 440 | // Enumerates the vertices adjacent to the provided vertex. 441 | func (g *labeledUndirected) AdjacentTo(vertex Vertex, f VertexStep) { 442 | g.mu.RLock() 443 | defer g.mu.RUnlock() 444 | 445 | eachVertexInAdjacencyList(g.list, vertex, f) 446 | } 447 | 448 | // Indicates whether or not the given edge is present in the graph. It matches 449 | // based solely on the presence of an edge, disregarding edge label. 450 | func (g *labeledUndirected) HasEdge(edge Edge) bool { 451 | g.mu.RLock() 452 | defer g.mu.RUnlock() 453 | 454 | // Spread it into two expressions to avoid evaluating the second if possible 455 | u, v := edge.Both() 456 | if _, exists := g.list[u][v]; exists { 457 | return true 458 | } else if _, exists := g.list[v][u]; exists { 459 | return true 460 | } 461 | return false 462 | } 463 | 464 | // Indicates whether or not the given labeled edge is present in the graph. 465 | // It will only match if the provided LabeledEdge has the same label as 466 | // the edge contained in the graph. 467 | func (g *labeledUndirected) HasLabeledEdge(edge LabeledEdge) bool { 468 | g.mu.RLock() 469 | defer g.mu.RUnlock() 470 | 471 | // Spread it into two expressions to avoid evaluating the second if possible 472 | u, v := edge.Both() 473 | if label, exists := g.list[u][v]; exists { 474 | return edge.Label() == label 475 | } else if label, exists := g.list[v][u]; exists { 476 | return edge.Label() == label 477 | } 478 | return false 479 | } 480 | 481 | // Returns the density of the graph. Density is the ratio of edge count to the 482 | // number of edges there would be in complete graph (maximum edge count). 483 | func (g *labeledUndirected) Density() float64 { 484 | g.mu.RLock() 485 | defer g.mu.RUnlock() 486 | 487 | order := g.Order() 488 | return 2 * float64(g.Size()) / float64(order*(order-1)) 489 | } 490 | 491 | // Removes a vertex from the graph. Also removes any edges of which that 492 | // vertex is a member. 493 | func (g *labeledUndirected) RemoveVertex(vertices ...Vertex) { 494 | if len(vertices) == 0 { 495 | return 496 | } 497 | 498 | g.mu.Lock() 499 | defer g.mu.Unlock() 500 | 501 | for _, vertex := range vertices { 502 | if g.hasVertex(vertex) { 503 | eachVertexInAdjacencyList(g.list, vertex, func(adjacent Vertex) (terminate bool) { 504 | delete(g.list[adjacent], vertex) 505 | return 506 | }) 507 | g.size -= len(g.list[vertex]) 508 | delete(g.list, vertex) 509 | } 510 | } 511 | return 512 | } 513 | 514 | // Adds edges to the graph. 515 | func (g *labeledUndirected) AddEdges(edges ...LabeledEdge) { 516 | if len(edges) == 0 { 517 | return 518 | } 519 | 520 | g.mu.Lock() 521 | defer g.mu.Unlock() 522 | 523 | g.addEdges(edges...) 524 | } 525 | 526 | // Adds a new edge to the graph. 527 | func (g *labeledUndirected) addEdges(edges ...LabeledEdge) { 528 | for _, edge := range edges { 529 | u, v := edge.Both() 530 | g.ensureVertex(u, v) 531 | 532 | if _, exists := g.list[u][v]; !exists { 533 | l := edge.Label() 534 | g.list[u][v] = l 535 | g.list[v][u] = l 536 | g.size++ 537 | } 538 | } 539 | } 540 | 541 | // Removes edges from the graph. This does NOT remove vertex members of the 542 | // removed edges. 543 | func (g *labeledUndirected) RemoveEdges(edges ...LabeledEdge) { 544 | if len(edges) == 0 { 545 | return 546 | } 547 | 548 | g.mu.Lock() 549 | defer g.mu.Unlock() 550 | 551 | for _, edge := range edges { 552 | s, t := edge.Both() 553 | if _, exists := g.list[s][t]; exists { 554 | delete(g.list[s], t) 555 | delete(g.list[t], s) 556 | g.size-- 557 | } 558 | } 559 | } 560 | -------------------------------------------------------------------------------- /graph/al/undirected.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | . "github.com/sdboyer/gogl" 5 | "gopkg.in/fatih/set.v0" 6 | ) 7 | 8 | type mutableUndirected struct { 9 | al_basic_mut 10 | } 11 | 12 | // Returns the degree of the provided vertex. If the vertex is not present in the 13 | // graph, the second return value will be false. 14 | func (g *mutableUndirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 15 | g.mu.RLock() 16 | defer g.mu.RUnlock() 17 | 18 | if exists = g.hasVertex(vertex); exists { 19 | degree = len(g.list[vertex]) 20 | } 21 | return 22 | } 23 | 24 | // Traverses the set of edges in the graph, passing each edge to the 25 | // provided closure. 26 | func (g *mutableUndirected) Edges(f EdgeStep) { 27 | g.mu.RLock() 28 | defer g.mu.RUnlock() 29 | 30 | visited := set.New(set.NonThreadSafe) 31 | 32 | for source, adjacent := range g.list { 33 | for target := range adjacent { 34 | e := NewEdge(source, target) 35 | if !visited.Has(NewEdge(target, source)) { 36 | visited.Add(e) 37 | if f(e) { 38 | return 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | // Enumerates the set of all edges incident to the provided vertex. 46 | func (g *mutableUndirected) IncidentTo(v Vertex, f EdgeStep) { 47 | g.mu.RLock() 48 | defer g.mu.RUnlock() 49 | 50 | if !g.hasVertex(v) { 51 | return 52 | } 53 | 54 | for adjacent := range g.list[v] { 55 | if f(NewEdge(v, adjacent)) { 56 | return 57 | } 58 | } 59 | } 60 | 61 | // Enumerates the vertices adjacent to the provided vertex. 62 | func (g *mutableUndirected) AdjacentTo(vertex Vertex, f VertexStep) { 63 | g.mu.RLock() 64 | defer g.mu.RUnlock() 65 | 66 | eachVertexInAdjacencyList(g.list, vertex, f) 67 | } 68 | 69 | // Indicates whether or not the given edge is present in the graph. 70 | func (g *mutableUndirected) HasEdge(edge Edge) bool { 71 | g.mu.RLock() 72 | defer g.mu.RUnlock() 73 | 74 | // Spread it into two expressions to avoid evaluating the second if possible 75 | u, v := edge.Both() 76 | if _, exists := g.list[u][v]; exists { 77 | return true 78 | } else if _, exists := g.list[v][u]; exists { 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | // Returns the density of the graph. Density is the ratio of edge count to the 85 | // number of edges there would be in complete graph (maximum edge count). 86 | func (g *mutableUndirected) Density() float64 { 87 | g.mu.RLock() 88 | defer g.mu.RUnlock() 89 | 90 | order := g.Order() 91 | return 2 * float64(g.Size()) / float64(order*(order-1)) 92 | } 93 | 94 | // Removes a vertex from the graph. Also removes any edges of which that 95 | // vertex is a member. 96 | func (g *mutableUndirected) RemoveVertex(vertices ...Vertex) { 97 | if len(vertices) == 0 { 98 | return 99 | } 100 | 101 | g.mu.Lock() 102 | defer g.mu.Unlock() 103 | 104 | for _, vertex := range vertices { 105 | if g.hasVertex(vertex) { 106 | eachVertexInAdjacencyList(g.list, vertex, func(adjacent Vertex) (terminate bool) { 107 | delete(g.list[adjacent], vertex) 108 | return 109 | }) 110 | g.size -= len(g.list[vertex]) 111 | delete(g.list, vertex) 112 | } 113 | } 114 | return 115 | } 116 | 117 | // Adds edges to the graph. 118 | func (g *mutableUndirected) AddEdges(edges ...Edge) { 119 | if len(edges) == 0 { 120 | return 121 | } 122 | 123 | g.mu.Lock() 124 | defer g.mu.Unlock() 125 | 126 | g.addEdges(edges...) 127 | } 128 | 129 | // Adds a new edge to the graph. 130 | func (g *mutableUndirected) addEdges(edges ...Edge) { 131 | for _, edge := range edges { 132 | u, v := edge.Both() 133 | g.ensureVertex(u, v) 134 | 135 | if _, exists := g.list[u][v]; !exists { 136 | g.list[u][v] = keyExists 137 | g.list[v][u] = keyExists 138 | g.size++ 139 | } 140 | } 141 | } 142 | 143 | // Removes edges from the graph. This does NOT remove vertex members of the 144 | // removed edges. 145 | func (g *mutableUndirected) RemoveEdges(edges ...Edge) { 146 | if len(edges) == 0 { 147 | return 148 | } 149 | 150 | g.mu.Lock() 151 | defer g.mu.Unlock() 152 | 153 | for _, edge := range edges { 154 | s, t := edge.Both() 155 | if _, exists := g.list[s][t]; exists { 156 | delete(g.list[s], t) 157 | delete(g.list[t], s) 158 | g.size-- 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /graph/al/weighted.go: -------------------------------------------------------------------------------- 1 | package al 2 | 3 | import ( 4 | "sync" 5 | 6 | . "github.com/sdboyer/gogl" 7 | "gopkg.in/fatih/set.v0" 8 | ) 9 | 10 | // This is implemented as an adjacency list, because those are simple. 11 | type baseWeighted struct { 12 | list map[Vertex]map[Vertex]float64 13 | size int 14 | mu sync.RWMutex 15 | } 16 | 17 | /* baseWeighted shared methods */ 18 | 19 | // Traverses the graph's vertices in random order, passing each vertex to the 20 | // provided closure. 21 | func (g *baseWeighted) Vertices(f VertexStep) { 22 | g.mu.RLock() 23 | defer g.mu.RUnlock() 24 | 25 | for v := range g.list { 26 | if f(v) { 27 | return 28 | } 29 | } 30 | } 31 | 32 | // Indicates whether or not the given vertex is present in the graph. 33 | func (g *baseWeighted) HasVertex(vertex Vertex) (exists bool) { 34 | g.mu.RLock() 35 | defer g.mu.RUnlock() 36 | 37 | exists = g.hasVertex(vertex) 38 | return 39 | } 40 | 41 | // Indicates whether or not the given vertex is present in the graph. 42 | func (g *baseWeighted) hasVertex(vertex Vertex) (exists bool) { 43 | _, exists = g.list[vertex] 44 | return 45 | } 46 | 47 | // Returns the order (number of vertices) in the graph. 48 | func (g *baseWeighted) Order() int { 49 | g.mu.RLock() 50 | defer g.mu.RUnlock() 51 | 52 | return len(g.list) 53 | } 54 | 55 | // Returns the size (number of edges) in the graph. 56 | func (g *baseWeighted) Size() int { 57 | return g.size 58 | } 59 | 60 | // Adds the provided vertices to the graph. If a provided vertex is 61 | // already present in the graph, it is a no-op (for that vertex only). 62 | func (g *baseWeighted) EnsureVertex(vertices ...Vertex) { 63 | if len(vertices) == 0 { 64 | return 65 | } 66 | 67 | g.mu.Lock() 68 | defer g.mu.Unlock() 69 | 70 | g.ensureVertex(vertices...) 71 | } 72 | 73 | // Adds the provided vertices to the graph. If a provided vertex is 74 | // already present in the graph, it is a no-op (for that vertex only). 75 | func (g *baseWeighted) ensureVertex(vertices ...Vertex) { 76 | for _, vertex := range vertices { 77 | if !g.hasVertex(vertex) { 78 | // TODO experiment with different lengths...possibly by analyzing existing density? 79 | g.list[vertex] = make(map[Vertex]float64, 10) 80 | } 81 | } 82 | 83 | return 84 | } 85 | 86 | /* DirectedWeighted implementation */ 87 | 88 | type weightedDirected struct { 89 | baseWeighted 90 | } 91 | 92 | // Returns the outdegree of the provided vertex. If the vertex is not present in the 93 | // graph, the second return value will be false. 94 | func (g *weightedDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) { 95 | g.mu.RLock() 96 | defer g.mu.RUnlock() 97 | 98 | if exists = g.hasVertex(vertex); exists { 99 | degree = len(g.list[vertex]) 100 | } 101 | return 102 | } 103 | 104 | // Returns the indegree of the provided vertex. If the vertex is not present in the 105 | // graph, the second return value will be false. 106 | // 107 | // Note that getting indegree is inefficient for directed adjacency lists; it requires 108 | // a full scan of the graph's edge set. 109 | func (g *weightedDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) { 110 | g.mu.RLock() 111 | defer g.mu.RUnlock() 112 | return inDegreeOf(g, vertex) 113 | } 114 | 115 | // Returns the degree of the given vertex, counting both in and out-edges. 116 | func (g *weightedDirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 117 | g.mu.RLock() 118 | defer g.mu.RUnlock() 119 | 120 | indegree, exists := inDegreeOf(g, vertex) 121 | outdegree, exists := g.OutDegreeOf(vertex) 122 | return indegree + outdegree, exists 123 | } 124 | 125 | // Enumerates the set of all edges incident to the provided vertex. 126 | func (g *weightedDirected) IncidentTo(v Vertex, f EdgeStep) { 127 | g.mu.RLock() 128 | defer g.mu.RUnlock() 129 | eachEdgeIncidentToDirected(g, v, f) 130 | } 131 | 132 | // Enumerates the vertices adjacent to the provided vertex. 133 | func (g *weightedDirected) AdjacentTo(start Vertex, f VertexStep) { 134 | g.mu.RLock() 135 | defer g.mu.RUnlock() 136 | 137 | g.IncidentTo(start, func(e Edge) bool { 138 | u, v := e.Both() 139 | if u == start { 140 | return f(v) 141 | } else { 142 | return f(u) 143 | } 144 | }) 145 | } 146 | 147 | // Enumerates the set of out-edges for the provided vertex. 148 | func (g *weightedDirected) ArcsFrom(v Vertex, f ArcStep) { 149 | g.mu.RLock() 150 | defer g.mu.RUnlock() 151 | 152 | if !g.hasVertex(v) { 153 | return 154 | } 155 | 156 | for adjacent, weight := range g.list[v] { 157 | if f(NewWeightedArc(v, adjacent, weight)) { 158 | return 159 | } 160 | } 161 | } 162 | 163 | func (g *weightedDirected) SuccessorsOf(v Vertex, f VertexStep) { 164 | g.mu.RLock() 165 | defer g.mu.RUnlock() 166 | 167 | eachVertexInAdjacencyList(g.list, v, f) 168 | } 169 | 170 | // Enumerates the set of in-edges for the provided vertex. 171 | func (g *weightedDirected) ArcsTo(v Vertex, f ArcStep) { 172 | g.mu.RLock() 173 | defer g.mu.RUnlock() 174 | 175 | if !g.hasVertex(v) { 176 | return 177 | } 178 | 179 | for candidate, adjacent := range g.list { 180 | for target, weight := range adjacent { 181 | if target == v { 182 | if f(NewWeightedArc(candidate, target, weight)) { 183 | return 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | func (g *weightedDirected) PredecessorsOf(v Vertex, f VertexStep) { 191 | g.mu.RLock() 192 | defer g.mu.RUnlock() 193 | 194 | eachPredecessorOf(g.list, v, f) 195 | } 196 | 197 | // Traverses the set of edges in the graph, passing each edge to the 198 | // provided closure. 199 | func (g *weightedDirected) Edges(f EdgeStep) { 200 | g.mu.RLock() 201 | defer g.mu.RUnlock() 202 | 203 | for source, adjacent := range g.list { 204 | for target, weight := range adjacent { 205 | if f(NewWeightedEdge(source, target, weight)) { 206 | return 207 | } 208 | } 209 | } 210 | } 211 | 212 | // Indicates whether or not the given edge is present in the graph. It matches 213 | // based solely on the presence of an edge, disregarding edge weight. 214 | func (g *weightedDirected) HasEdge(edge Edge) bool { 215 | g.mu.RLock() 216 | defer g.mu.RUnlock() 217 | 218 | u, v := edge.Both() 219 | _, exists := g.list[u][v] 220 | if !exists { 221 | _, exists = g.list[v][u] 222 | } 223 | return exists 224 | } 225 | 226 | // Indicates whether or not the given arc is present in the graph. 227 | func (g *weightedDirected) HasArc(arc Arc) bool { 228 | g.mu.RLock() 229 | defer g.mu.RUnlock() 230 | 231 | _, exists := g.list[arc.Source()][arc.Target()] 232 | return exists 233 | } 234 | 235 | // Indicates whether or not the given weighted edge is present in the graph. 236 | // It will only match if the provided WeightedEdge has the same weight as 237 | // the edge contained in the graph. 238 | func (g *weightedDirected) HasWeightedEdge(edge WeightedEdge) bool { 239 | g.mu.RLock() 240 | defer g.mu.RUnlock() 241 | 242 | u, v := edge.Both() 243 | if weight, exists := g.list[u][v]; exists { 244 | return weight == edge.Weight() 245 | } else if weight, exists = g.list[v][u]; exists { 246 | return weight == edge.Weight() 247 | } 248 | return false 249 | } 250 | 251 | // Indicates whether or not the given weighted arc is present in the graph. 252 | // It will only match if the provided LabeledEdge has the same label as 253 | // the edge contained in the graph. 254 | func (g *weightedDirected) HasWeightedArc(arc WeightedArc) bool { 255 | g.mu.RLock() 256 | defer g.mu.RUnlock() 257 | 258 | if weight, exists := g.list[arc.Source()][arc.Target()]; exists { 259 | return weight == arc.Weight() 260 | } 261 | return false 262 | } 263 | 264 | // Returns the density of the graph. Density is the ratio of edge count to the 265 | // number of edges there would be in complete graph (maximum edge count). 266 | func (g *weightedDirected) Density() float64 { 267 | g.mu.RLock() 268 | defer g.mu.RUnlock() 269 | 270 | order := g.Order() 271 | return float64(g.Size()) / float64(order*(order-1)) 272 | } 273 | 274 | // Removes a vertex from the graph. Also removes any edges of which that 275 | // vertex is a member. 276 | func (g *weightedDirected) RemoveVertex(vertices ...Vertex) { 277 | if len(vertices) == 0 { 278 | return 279 | } 280 | 281 | g.mu.Lock() 282 | defer g.mu.Unlock() 283 | 284 | for _, vertex := range vertices { 285 | if g.hasVertex(vertex) { 286 | g.size -= len(g.list[vertex]) 287 | delete(g.list, vertex) 288 | 289 | for _, adjacent := range g.list { 290 | if _, has := adjacent[vertex]; has { 291 | delete(adjacent, vertex) 292 | g.size-- 293 | } 294 | } 295 | } 296 | } 297 | return 298 | } 299 | 300 | // Adds arcs to the graph. 301 | func (g *weightedDirected) AddArcs(arcs ...WeightedArc) { 302 | if len(arcs) == 0 { 303 | return 304 | } 305 | 306 | g.mu.Lock() 307 | defer g.mu.Unlock() 308 | 309 | g.addArcs(arcs...) 310 | } 311 | 312 | // Adds a new arc to the graph. 313 | func (g *weightedDirected) addArcs(arcs ...WeightedArc) { 314 | for _, arc := range arcs { 315 | g.ensureVertex(arc.Source(), arc.Target()) 316 | 317 | if _, exists := g.list[arc.Source()][arc.Target()]; !exists { 318 | g.list[arc.Source()][arc.Target()] = arc.Weight() 319 | g.size++ 320 | } 321 | } 322 | } 323 | 324 | // Removes arcs from the graph. This does NOT remove vertex members of the 325 | // removed arcs. 326 | func (g *weightedDirected) RemoveArcs(arcs ...WeightedArc) { 327 | if len(arcs) == 0 { 328 | return 329 | } 330 | 331 | g.mu.Lock() 332 | defer g.mu.Unlock() 333 | 334 | for _, arc := range arcs { 335 | s, t := arc.Both() 336 | if _, exists := g.list[s][t]; exists { 337 | delete(g.list[s], t) 338 | g.size-- 339 | } 340 | } 341 | } 342 | 343 | func (g *weightedDirected) Transpose() Digraph { 344 | g.mu.RLock() 345 | defer g.mu.RUnlock() 346 | 347 | g2 := &weightedDirected{} 348 | g2.list = make(map[Vertex]map[Vertex]float64) 349 | 350 | // Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps 351 | startcap := int(g.Size() / g.Order()) 352 | 353 | for source, adjacent := range g.list { 354 | if !g2.hasVertex(source) { 355 | g2.list[source] = make(map[Vertex]float64, startcap+1) 356 | } 357 | 358 | for target, weight := range adjacent { 359 | if !g2.hasVertex(target) { 360 | g2.list[target] = make(map[Vertex]float64, startcap+1) 361 | } 362 | g2.list[target][source] = weight 363 | } 364 | } 365 | 366 | return g2 367 | } 368 | 369 | /* UndirectedWeighted implementation */ 370 | 371 | type weightedUndirected struct { 372 | baseWeighted 373 | } 374 | 375 | // Returns the degree of the provided vertex. If the vertex is not present in the 376 | // graph, the second return value will be false. 377 | func (g *weightedUndirected) DegreeOf(vertex Vertex) (degree int, exists bool) { 378 | g.mu.RLock() 379 | defer g.mu.RUnlock() 380 | 381 | if exists = g.hasVertex(vertex); exists { 382 | degree = len(g.list[vertex]) 383 | } 384 | return 385 | } 386 | 387 | // Traverses the set of edges in the graph, passing each edge to the 388 | // provided closure. 389 | func (g *weightedUndirected) Edges(f EdgeStep) { 390 | g.mu.RLock() 391 | defer g.mu.RUnlock() 392 | 393 | visited := set.New(set.NonThreadSafe) 394 | 395 | var e WeightedEdge 396 | for source, adjacent := range g.list { 397 | for target, weight := range adjacent { 398 | e = NewWeightedEdge(source, target, weight) 399 | if !visited.Has(NewEdge(e.Both())) { 400 | visited.Add(NewEdge(target, source)) 401 | if f(e) { 402 | return 403 | } 404 | } 405 | } 406 | } 407 | } 408 | 409 | // Traverses the set of arcs in the graph, passing each arc to the 410 | // provided closure. 411 | func (g *weightedDirected) Arcs(f ArcStep) { 412 | g.mu.RLock() 413 | defer g.mu.RUnlock() 414 | 415 | for source, adjacent := range g.list { 416 | for target, weight := range adjacent { 417 | if f(NewWeightedArc(source, target, weight)) { 418 | return 419 | } 420 | } 421 | } 422 | } 423 | 424 | // Enumerates the set of all edges incident to the provided vertex. 425 | func (g *weightedUndirected) IncidentTo(v Vertex, f EdgeStep) { 426 | g.mu.RLock() 427 | defer g.mu.RUnlock() 428 | 429 | if !g.hasVertex(v) { 430 | return 431 | } 432 | 433 | for adjacent, weight := range g.list[v] { 434 | if f(NewWeightedEdge(v, adjacent, weight)) { 435 | return 436 | } 437 | } 438 | } 439 | 440 | // Enumerates the vertices adjacent to the provided vertex. 441 | func (g *weightedUndirected) AdjacentTo(vertex Vertex, f VertexStep) { 442 | g.mu.RLock() 443 | defer g.mu.RUnlock() 444 | 445 | eachVertexInAdjacencyList(g.list, vertex, f) 446 | } 447 | 448 | // Indicates whether or not the given edge is present in the graph. It matches 449 | // based solely on the presence of an edge, disregarding edge weight. 450 | func (g *weightedUndirected) HasEdge(edge Edge) bool { 451 | g.mu.RLock() 452 | defer g.mu.RUnlock() 453 | 454 | // Spread it into two expressions to avoid evaluating the second if possible 455 | u, v := edge.Both() 456 | if _, exists := g.list[u][v]; exists { 457 | return true 458 | } else if _, exists := g.list[v][u]; exists { 459 | return true 460 | } 461 | return false 462 | } 463 | 464 | // Indicates whether or not the given weighted edge is present in the graph. 465 | // It will only match if the provided WeightedEdge has the same weight as 466 | // the edge contained in the graph. 467 | func (g *weightedUndirected) HasWeightedEdge(edge WeightedEdge) bool { 468 | g.mu.RLock() 469 | defer g.mu.RUnlock() 470 | 471 | // Spread it into two expressions to avoid evaluating the second if possible 472 | u, v := edge.Both() 473 | if weight, exists := g.list[u][v]; exists { 474 | return edge.Weight() == weight 475 | } else if weight, exists := g.list[v][u]; exists { 476 | return edge.Weight() == weight 477 | } 478 | return false 479 | } 480 | 481 | // Returns the density of the graph. Density is the ratio of edge count to the 482 | // number of edges there would be in complete graph (maximum edge count). 483 | func (g *weightedUndirected) Density() float64 { 484 | g.mu.RLock() 485 | defer g.mu.RUnlock() 486 | 487 | order := g.Order() 488 | return 2 * float64(g.Size()) / float64(order*(order-1)) 489 | } 490 | 491 | // Removes a vertex from the graph. Also removes any edges of which that 492 | // vertex is a member. 493 | func (g *weightedUndirected) RemoveVertex(vertices ...Vertex) { 494 | if len(vertices) == 0 { 495 | return 496 | } 497 | 498 | g.mu.Lock() 499 | defer g.mu.Unlock() 500 | 501 | for _, vertex := range vertices { 502 | if g.hasVertex(vertex) { 503 | eachVertexInAdjacencyList(g.list, vertex, func(adjacent Vertex) (terminate bool) { 504 | delete(g.list[adjacent], vertex) 505 | return 506 | }) 507 | g.size -= len(g.list[vertex]) 508 | delete(g.list, vertex) 509 | } 510 | } 511 | return 512 | } 513 | 514 | // Adds edges to the graph. 515 | func (g *weightedUndirected) AddEdges(edges ...WeightedEdge) { 516 | if len(edges) == 0 { 517 | return 518 | } 519 | 520 | g.mu.Lock() 521 | defer g.mu.Unlock() 522 | 523 | g.addEdges(edges...) 524 | } 525 | 526 | // Adds a new edge to the graph. 527 | func (g *weightedUndirected) addEdges(edges ...WeightedEdge) { 528 | for _, edge := range edges { 529 | u, v := edge.Both() 530 | g.ensureVertex(u, v) 531 | 532 | if _, exists := g.list[u][v]; !exists { 533 | w := edge.Weight() 534 | g.list[u][v] = w 535 | g.list[v][u] = w 536 | g.size++ 537 | } 538 | } 539 | } 540 | 541 | // Removes edges from the graph. This does NOT remove vertex members of the 542 | // removed edges. 543 | func (g *weightedUndirected) RemoveEdges(edges ...WeightedEdge) { 544 | if len(edges) == 0 { 545 | return 546 | } 547 | 548 | g.mu.Lock() 549 | defer g.mu.Unlock() 550 | 551 | for _, edge := range edges { 552 | s, t := edge.Both() 553 | if _, exists := g.list[s][t]; exists { 554 | delete(g.list[s], t) 555 | delete(g.list[t], s) 556 | g.size-- 557 | } 558 | } 559 | } 560 | -------------------------------------------------------------------------------- /null.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | // The null graph is a graph without any edges or vertices. It implements all possible (non-mutable) graph interfaces. 8 | // 9 | // In effect, it is the zero-value of all possible graph types. 10 | const NullGraph = nullGraph(false) 11 | 12 | type nullGraph bool 13 | 14 | var _ Graph = nullGraph(false) 15 | var _ Digraph = nullGraph(false) 16 | var _ SimpleGraph = nullGraph(false) 17 | var _ WeightedGraph = nullGraph(false) 18 | var _ LabeledGraph = nullGraph(false) 19 | var _ DataGraph = nullGraph(false) 20 | 21 | func (g nullGraph) Vertices(f VertexStep) {} 22 | func (g nullGraph) Edges(f EdgeStep) {} 23 | func (g nullGraph) Arcs(f ArcStep) {} 24 | func (g nullGraph) IncidentTo(Vertex, EdgeStep) {} 25 | func (g nullGraph) ArcsFrom(Vertex, ArcStep) {} 26 | func (g nullGraph) PredecessorsOf(Vertex, VertexStep) {} 27 | func (g nullGraph) ArcsTo(Vertex, ArcStep) {} 28 | func (g nullGraph) SuccessorsOf(Vertex, VertexStep) {} 29 | func (g nullGraph) AdjacentTo(start Vertex, f VertexStep) {} 30 | 31 | func (g nullGraph) HasVertex(v Vertex) bool { 32 | return false 33 | } 34 | 35 | func (g nullGraph) InDegreeOf(Vertex) (degree int, exists bool) { 36 | return 0, false 37 | } 38 | 39 | func (g nullGraph) OutDegreeOf(Vertex) (degree int, exists bool) { 40 | return 0, false 41 | } 42 | 43 | func (g nullGraph) DegreeOf(Vertex) (degree int, exists bool) { 44 | return 0, false 45 | } 46 | 47 | func (g nullGraph) HasEdge(e Edge) bool { 48 | return false 49 | } 50 | 51 | func (g nullGraph) HasArc(e Arc) bool { 52 | return false 53 | } 54 | 55 | func (g nullGraph) HasWeightedEdge(e WeightedEdge) bool { 56 | return false 57 | } 58 | 59 | func (g nullGraph) HasLabeledEdge(e LabeledEdge) bool { 60 | return false 61 | } 62 | 63 | func (g nullGraph) HasDataEdge(e DataEdge) bool { 64 | return false 65 | } 66 | 67 | func (g nullGraph) Density() float64 { 68 | return math.NaN() 69 | } 70 | 71 | func (g nullGraph) Transpose() Digraph { 72 | return g 73 | } 74 | -------------------------------------------------------------------------------- /null_test.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | import ( 4 | . "github.com/sdboyer/gocheck" 5 | "math" 6 | ) 7 | 8 | type NullGraphSuite bool 9 | 10 | var _ = Suite(NullGraphSuite(false)) 11 | 12 | func (s NullGraphSuite) TestEnumerators(c *C) { 13 | NullGraph.Vertices(func(v Vertex) (terminate bool) { 14 | c.Error("The NullGraph should not have any vertices.") 15 | return 16 | }) 17 | 18 | NullGraph.Edges(func(e Edge) (terminate bool) { 19 | c.Error("The NullGraph should not have any edges.") 20 | return 21 | }) 22 | 23 | NullGraph.IncidentTo("foo", func(e Edge) (terminate bool) { 24 | c.Error("The NullGraph should be empty of edges and vertices.") 25 | return 26 | }) 27 | 28 | NullGraph.AdjacentTo("foo", func(v Vertex) (terminate bool) { 29 | c.Error("The NullGraph should be empty of edges and vertices.") 30 | return 31 | }) 32 | 33 | NullGraph.ArcsFrom("foo", func(e Arc) (terminate bool) { 34 | c.Error("The NullGraph should be empty of edges and vertices.") 35 | return 36 | }) 37 | 38 | NullGraph.ArcsTo("foo", func(e Arc) (terminate bool) { 39 | c.Error("The NullGraph should be empty of edges and vertices.") 40 | return 41 | }) 42 | } 43 | 44 | func (s NullGraphSuite) TestMembership(c *C) { 45 | c.Assert(NullGraph.HasVertex("foo"), Equals, false) // must always be false 46 | c.Assert(NullGraph.HasEdge(NewEdge("qux", "quark")), Equals, false) // must always be false 47 | c.Assert(NullGraph.HasWeightedEdge(NewWeightedEdge("qux", "quark", 0)), Equals, false) // must always be false 48 | c.Assert(NullGraph.HasLabeledEdge(NewLabeledEdge("qux", "quark", "")), Equals, false) // must always be false 49 | c.Assert(NullGraph.HasDataEdge(NewDataEdge("qux", "quark", func() {})), Equals, false) // must always be false 50 | } 51 | 52 | func (s NullGraphSuite) TestDegree(c *C) { 53 | deg, exists := NullGraph.DegreeOf("foo") 54 | c.Assert(exists, Equals, false) // vertex is not present in graph 55 | c.Assert(deg, Equals, 0) // always will have degree 0 56 | 57 | deg, exists = NullGraph.InDegreeOf("foo") 58 | c.Assert(exists, Equals, false) // vertex is not present in graph 59 | c.Assert(deg, Equals, 0) // always will have indegree 0 60 | 61 | deg, exists = NullGraph.OutDegreeOf("foo") 62 | c.Assert(exists, Equals, false) // vertex is not present in graph 63 | c.Assert(deg, Equals, 0) // always will have outdegree 0 64 | } 65 | 66 | func (s NullGraphSuite) TestSizingOps(c *C) { 67 | c.Assert(math.IsNaN(NullGraph.Density()), Equals, true) 68 | } 69 | 70 | func (s NullGraphSuite) TestTranspose(c *C) { 71 | c.Assert(NullGraph.Transpose(), Equals, NullGraph) 72 | } 73 | -------------------------------------------------------------------------------- /rand/bernoulli.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | stdrand "math/rand" 5 | 6 | "github.com/sdboyer/gogl" 7 | ) 8 | 9 | // Generates a random graph of vertex count n with probability ρ of an edge existing between any two vertices. 10 | // 11 | // This produces simple graphs only - no loops, no multiple edges. Graphs can be either directed or undirected, governed 12 | // by the appropriately named parameter. 13 | // 14 | // ρ must be a float64 in the range [0.0,1.0) - that is, 0.0 <= ρ < 1.0 - else, panic. 15 | // 16 | // If a stable graph is requested (stable == true), then the edge set presented by calling Edges() on the returned graph 17 | // will be the same on every call. To provide stability, however, a memory allocation of n^2 * (int width) bytes 18 | // is required to store the generated graph. 19 | // 20 | // Unstable graphs will create a new probabilistic edge set on the fly each time Edges(). It thus makes only minimal 21 | // allocations, but is still CPU intensive for successive runs (and produces a different edge set). Given these 22 | // characteristics, unstable graphs should always be used for single-use random graphs. 23 | // 24 | // Binomial trials require a rand source. If none is provided, the stdlib math's global rand source is used. 25 | func BernoulliDistribution(n uint, ρ float64, directed bool, stable bool, src stdrand.Source) gogl.GraphSource { 26 | if ρ < 0.0 || ρ >= 1.0 { 27 | panic("ρ must be in the range [0.0,1.0).") 28 | } 29 | 30 | var f bTrial 31 | 32 | if src == nil { 33 | f = func(ρ float64) bool { 34 | return stdrand.Float64() < ρ 35 | } 36 | } else { 37 | r := stdrand.New(src) 38 | f = func(ρ float64) bool { 39 | return r.Float64() < ρ 40 | } 41 | } 42 | 43 | if stable { 44 | g := stableBernoulliGraph{order: n, ρ: ρ, trial: f} 45 | if directed { 46 | return &stableBernoulliDigraph{g} 47 | } else { 48 | return &g 49 | } 50 | } else { 51 | g := unstableBernoulliGraph{order: n, ρ: ρ, trial: f} 52 | if directed { 53 | return &unstableBernoulliDigraph{g} 54 | } else { 55 | return &g 56 | } 57 | } 58 | } 59 | 60 | type bTrial func(ρ float64) bool 61 | 62 | type stableBernoulliGraph struct { 63 | order uint 64 | ρ float64 65 | trial bTrial 66 | size int 67 | list [][]bool 68 | } 69 | 70 | func (g *stableBernoulliGraph) Vertices(f gogl.VertexStep) { 71 | o := int(g.order) 72 | for i := 0; i < o; i++ { 73 | if f(i) { 74 | return 75 | } 76 | } 77 | } 78 | 79 | func (g *stableBernoulliGraph) Edges(f gogl.EdgeStep) { 80 | if g.list == nil { 81 | g.list = make([][]bool, g.order, g.order) 82 | 83 | // Wrapping edge step function; records edges into the adjacency list, then passes edge along 84 | ff := func(e gogl.Edge) bool { 85 | uv, vv := e.Both() 86 | u, v := uv.(int), vv.(int) 87 | if g.list[u] == nil { 88 | g.list[u] = make([]bool, g.order, g.order) 89 | } 90 | g.list[u][v] = true 91 | g.size++ 92 | return f(e) 93 | } 94 | 95 | bernoulliEdgeCreator(ff, int(g.order), g.ρ, g.trial) 96 | } else { 97 | var e gogl.Edge 98 | for u, adj := range g.list { 99 | for v, exists := range adj { 100 | if exists { 101 | e = gogl.NewEdge(u, v) 102 | if f(e) { 103 | return 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | func (g *stableBernoulliGraph) Order() int { 112 | return int(g.order) 113 | } 114 | 115 | func (g *stableBernoulliGraph) Size() int { 116 | return g.size 117 | } 118 | 119 | type stableBernoulliDigraph struct { 120 | stableBernoulliGraph 121 | } 122 | 123 | func (g *stableBernoulliDigraph) Edges(f gogl.EdgeStep) { 124 | if g.list == nil { 125 | g.list = make([][]bool, g.order, g.order) 126 | 127 | // Wrapping edge step function; records edges into the adjacency list, then passes edge along 128 | ff := func(e gogl.Arc) bool { 129 | if g.list[e.Source().(int)] == nil { 130 | g.list[e.Source().(int)] = make([]bool, g.order, g.order) 131 | } 132 | g.list[e.Source().(int)][e.Target().(int)] = true 133 | g.size++ 134 | return f(e) 135 | } 136 | 137 | bernoulliArcCreator(ff, int(g.order), g.ρ, g.trial) 138 | } else { 139 | var e gogl.Arc 140 | for u, adj := range g.list { 141 | for v, exists := range adj { 142 | if exists { 143 | e = gogl.NewArc(u, v) 144 | if f(e) { 145 | return 146 | } 147 | } 148 | } 149 | } 150 | } 151 | } 152 | 153 | func (g *stableBernoulliDigraph) Arcs(f gogl.ArcStep) { 154 | if g.list == nil { 155 | g.list = make([][]bool, g.order, g.order) 156 | 157 | // Wrapping edge step function; records edges into the adjacency list, then passes edge along 158 | ff := func(e gogl.Arc) bool { 159 | if g.list[e.Source().(int)] == nil { 160 | g.list[e.Source().(int)] = make([]bool, g.order, g.order) 161 | } 162 | g.list[e.Source().(int)][e.Target().(int)] = true 163 | g.size++ 164 | return f(e) 165 | } 166 | 167 | bernoulliArcCreator(ff, int(g.order), g.ρ, g.trial) 168 | } else { 169 | var e gogl.Arc 170 | for u, adj := range g.list { 171 | for v, exists := range adj { 172 | if exists { 173 | e = gogl.NewArc(u, v) 174 | if f(e) { 175 | return 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | 183 | type unstableBernoulliGraph struct { 184 | order uint 185 | ρ float64 186 | trial bTrial 187 | } 188 | 189 | func (g unstableBernoulliGraph) Vertices(f gogl.VertexStep) { 190 | o := int(g.order) 191 | for i := 0; i < o; i++ { 192 | if f(i) { 193 | return 194 | } 195 | } 196 | } 197 | 198 | func (g unstableBernoulliGraph) Edges(f gogl.EdgeStep) { 199 | bernoulliEdgeCreator(f, int(g.order), g.ρ, g.trial) 200 | } 201 | 202 | func (g unstableBernoulliGraph) Order() int { 203 | return int(g.order) 204 | } 205 | 206 | type unstableBernoulliDigraph struct { 207 | unstableBernoulliGraph 208 | } 209 | 210 | func (g unstableBernoulliDigraph) Arcs(f gogl.ArcStep) { 211 | bernoulliArcCreator(f, int(g.order), g.ρ, g.trial) 212 | } 213 | 214 | func bernoulliEdgeCreator(el gogl.EdgeStep, order int, ρ float64, cmp bTrial) { 215 | var e gogl.Edge 216 | for u := 0; u < order; u++ { 217 | // Set target vertex to one more than current source vertex. This guarantees 218 | // we only evaluate each unique edge pair once, as gogl's implicit contract requires. 219 | for v := u + 0; v < order; v++ { 220 | if cmp(ρ) { 221 | e = gogl.NewEdge(u, v) 222 | if el(e) { 223 | return 224 | } 225 | } 226 | } 227 | } 228 | } 229 | 230 | func bernoulliArcCreator(el gogl.ArcStep, order int, ρ float64, cmp bTrial) { 231 | var e gogl.Arc 232 | for u := 0; u < order; u++ { 233 | for v := 0; v < order; v++ { 234 | if u != v && cmp(ρ) { 235 | e = gogl.NewArc(u, v) 236 | if el(e) { 237 | return 238 | } 239 | } 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /rand/bernoulli_test.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "fmt" 5 | stdrand "math/rand" 6 | "testing" 7 | "time" 8 | 9 | . "github.com/sdboyer/gocheck" 10 | "github.com/sdboyer/gogl" 11 | "gopkg.in/fatih/set.v0" 12 | ) 13 | 14 | var fml = fmt.Println 15 | 16 | func TestRand(t *testing.T) { TestingT(t) } 17 | 18 | type BernoulliTest struct { 19 | graphs map[string]gogl.GraphSource 20 | } 21 | 22 | var _ = Suite(&BernoulliTest{}) 23 | 24 | func (s *BernoulliTest) SetUpSuite(c *C) { 25 | r := stdrand.NewSource(time.Now().UnixNano()) 26 | s.graphs = map[string]gogl.GraphSource{ 27 | "dir_stable": BernoulliDistribution(10, 0.5, true, true, r), 28 | "und_stable": BernoulliDistribution(10, 0.5, false, true, r), 29 | "dir_unstable": BernoulliDistribution(10, 0.5, true, false, r), 30 | "und_unstable": BernoulliDistribution(10, 0.5, false, false, r), 31 | "und_unstable_nosrc": BernoulliDistribution(10, 0.5, false, false, nil), 32 | } 33 | } 34 | 35 | func (s *BernoulliTest) TestLengthChecks(c *C) { 36 | c.Assert(gogl.Order(s.graphs["dir_stable"]), Equals, 10) 37 | c.Assert(gogl.Order(s.graphs["und_stable"]), Equals, 10) 38 | c.Assert(gogl.Order(s.graphs["dir_unstable"]), Equals, 10) 39 | c.Assert(gogl.Order(s.graphs["und_unstable"]), Equals, 10) 40 | c.Assert(gogl.Order(s.graphs["und_unstable_nosrc"]), Equals, 10) 41 | } 42 | 43 | func (s *BernoulliTest) TestProbabilityRange(c *C) { 44 | f1 := func() { 45 | BernoulliDistribution(1, -0.0000001, true, true, nil) 46 | } 47 | 48 | f2 := func() { 49 | BernoulliDistribution(1, 1.0, true, true, nil) 50 | } 51 | c.Assert(f1, PanicMatches, "ρ must be in the range \\[0\\.0,1\\.0\\).") 52 | c.Assert(f2, PanicMatches, "ρ must be in the range \\[0\\.0,1\\.0\\).") 53 | } 54 | 55 | func (s *BernoulliTest) TestVertices(c *C) { 56 | sl := make([]int, 0, 50) 57 | 58 | for _, g := range s.graphs { 59 | g.Vertices(func(v gogl.Vertex) (terminate bool) { 60 | sl = append(sl, v.(int)) 61 | return 62 | }) 63 | 64 | } 65 | 66 | c.Assert(len(sl), Equals, 50) 67 | 68 | for k, v := range sl { 69 | c.Assert(k%10, Equals, v) 70 | } 71 | } 72 | 73 | func (s *BernoulliTest) TestVerticesTermination(c *C) { 74 | var hit int 75 | s.graphs["dir_stable"].Vertices(func(v gogl.Vertex) bool { 76 | hit++ 77 | return true 78 | }) 79 | 80 | c.Assert(hit, Equals, 1) 81 | 82 | s.graphs["dir_unstable"].Vertices(func(v gogl.Vertex) bool { 83 | hit++ 84 | return true 85 | }) 86 | 87 | c.Assert(hit, Equals, 2) 88 | } 89 | 90 | func (s *BernoulliTest) TestEdgesCount(c *C) { 91 | // Given that this is a rand count, our testing options are curtailed 92 | for gn, g := range s.graphs { 93 | hit := 0 94 | g.Edges(func(e gogl.Edge) (terminate bool) { 95 | hit++ 96 | return 97 | }) 98 | 99 | switch gn { 100 | case "dir_stable", "dir_unstable": 101 | c.Assert(hit <= 90, Equals, true) 102 | c.Assert(hit >= 0, Equals, true) 103 | case "und_stable", "und_unstable", "und_unstable_nosrc": 104 | c.Assert(hit <= 45, Equals, true) 105 | c.Assert(hit >= 0, Equals, true) 106 | } 107 | } 108 | } 109 | 110 | func (s *BernoulliTest) TestEdgesStability(c *C) { 111 | setd := set.New(set.NonThreadSafe) 112 | setu := set.New(set.NonThreadSafe) 113 | var hitu, hitd int 114 | 115 | dg := BernoulliDistribution(10, 0.5, true, true, nil) 116 | dg.Edges(func(e gogl.Edge) (terminate bool) { 117 | setd.Add(e) 118 | return 119 | }) 120 | 121 | dg.Edges(func(e gogl.Edge) (terminate bool) { 122 | c.Assert(setd.Has(e), Equals, true) 123 | hitd++ 124 | return 125 | }) 126 | 127 | c.Assert(setd.Size(), Equals, hitd) 128 | c.Assert(dg.(gogl.EdgeCounter).Size(), Equals, hitd) 129 | 130 | ug := BernoulliDistribution(10, 0.5, false, true, nil) 131 | ug.Edges(func(e gogl.Edge) (terminate bool) { 132 | setu.Add(e) 133 | return 134 | }) 135 | 136 | ug.Edges(func(e gogl.Edge) (terminate bool) { 137 | c.Assert(setu.Has(e), Equals, true) 138 | hitu++ 139 | return 140 | }) 141 | 142 | c.Assert(setu.Size(), Equals, hitu) 143 | c.Assert(ug.(gogl.EdgeCounter).Size(), Equals, hitu) 144 | 145 | } 146 | 147 | func (s *BernoulliTest) TestEdgesTermination(c *C) { 148 | var hit int 149 | s.graphs["dir_unstable"].Edges(func(e gogl.Edge) bool { 150 | hit++ 151 | return true 152 | }) 153 | 154 | c.Assert(hit, Equals, 1) 155 | 156 | s.graphs["und_unstable"].Edges(func(e gogl.Edge) bool { 157 | hit++ 158 | return true 159 | }) 160 | 161 | c.Assert(hit, Equals, 2) 162 | 163 | gogl.CollectEdges(s.graphs["und_stable"]) // To populate the cache 164 | s.graphs["und_stable"].Edges(func(e gogl.Edge) bool { 165 | hit++ 166 | return true 167 | }) 168 | 169 | c.Assert(hit, Equals, 3) 170 | 171 | gogl.CollectEdges(s.graphs["dir_stable"]) 172 | s.graphs["dir_stable"].Edges(func(e gogl.Edge) bool { 173 | hit++ 174 | return true 175 | }) 176 | c.Assert(hit, Equals, 4) 177 | } 178 | 179 | func (s *BernoulliTest) TestArcsStability(c *C) { 180 | setd := set.New(set.NonThreadSafe) 181 | var hitd int 182 | 183 | g := BernoulliDistribution(10, 0.5, true, true, nil).(gogl.DigraphSource) 184 | g.Arcs(func(e gogl.Arc) (terminate bool) { 185 | setd.Add(e) 186 | return 187 | }) 188 | 189 | g.Arcs(func(e gogl.Arc) (terminate bool) { 190 | c.Assert(setd.Has(e), Equals, true) 191 | hitd++ 192 | return 193 | }) 194 | 195 | c.Assert(setd.Size(), Equals, hitd) 196 | c.Assert(g.(gogl.EdgeCounter).Size(), Equals, hitd) 197 | } 198 | 199 | func (s *BernoulliTest) TestArcsTermination(c *C) { 200 | var hit int 201 | s.graphs["dir_unstable"].(gogl.DigraphSource).Arcs(func(e gogl.Arc) bool { 202 | hit++ 203 | return true 204 | }) 205 | c.Assert(hit, Equals, 1) 206 | 207 | gogl.CollectEdges(s.graphs["dir_stable"]) 208 | s.graphs["dir_stable"].(gogl.DigraphSource).Arcs(func(e gogl.Arc) bool { 209 | hit++ 210 | return true 211 | }) 212 | c.Assert(hit, Equals, 2) 213 | 214 | s.graphs["dir_stable"].Edges(func(e gogl.Edge) bool { 215 | hit++ 216 | return true 217 | }) 218 | c.Assert(hit, Equals, 3) 219 | } 220 | -------------------------------------------------------------------------------- /spec/graph_spec.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/kr/pretty" 9 | . "github.com/sdboyer/gocheck" 10 | . "github.com/sdboyer/gogl" 11 | "gopkg.in/fatih/set.v0" 12 | ) 13 | 14 | ///////////////////////////////////////////////////////////////////// 15 | // 16 | // GRAPH FIXTURES 17 | // 18 | ///////////////////////////////////////////////////////////////////// 19 | 20 | type loopArc struct { 21 | v Vertex 22 | } 23 | 24 | func (e loopArc) Both() (Vertex, Vertex) { 25 | return e.v, e.v 26 | } 27 | 28 | func (e loopArc) Source() Vertex { 29 | return e.v 30 | } 31 | 32 | func (e loopArc) Target() Vertex { 33 | return e.v 34 | } 35 | 36 | type loopArcList []Arc 37 | 38 | func (el loopArcList) Vertices(fn VertexStep) { 39 | set := set.New(set.NonThreadSafe) 40 | 41 | for _, e := range el { 42 | set.Add(e.Both()) 43 | } 44 | 45 | for _, v := range set.List() { 46 | if fn(v) { 47 | return 48 | } 49 | } 50 | } 51 | 52 | func (el loopArcList) Arcs(fn ArcStep) { 53 | for _, e := range el { 54 | if _, ok := e.(loopArc); !ok { 55 | if fn(e) { 56 | return 57 | } 58 | } 59 | } 60 | } 61 | 62 | func (el loopArcList) Edges(fn EdgeStep) { 63 | for _, e := range el { 64 | if _, ok := e.(loopArc); !ok { 65 | if fn(e) { 66 | return 67 | } 68 | } 69 | } 70 | } 71 | 72 | var GraphFixtures = map[string]GraphSource{ 73 | // TODO improve naming basis/patterns for these 74 | "arctest": ArcList{ 75 | NewArc("foo", "bar"), 76 | NewArc("bar", "baz"), 77 | NewArc("foo", "qux"), 78 | NewArc("qux", "bar"), 79 | }, 80 | "pair": ArcList{ 81 | NewArc(1, 2), 82 | }, 83 | "2e3v": ArcList{ 84 | NewArc("foo", "bar"), 85 | NewArc("bar", "baz"), 86 | }, 87 | "3e4v": ArcList{ 88 | NewArc("foo", "bar"), 89 | NewArc("bar", "baz"), 90 | NewArc("foo", "qux"), 91 | }, 92 | "3e5v1i": loopArcList{ 93 | NewArc("foo", "bar"), 94 | NewArc("bar", "baz"), 95 | NewArc("foo", "qux"), 96 | loopArc{"isolate"}, 97 | }, 98 | "w-2e3v": WeightedArcList{ 99 | NewWeightedArc(1, 2, 5.23), 100 | NewWeightedArc(2, 3, 5.821), 101 | }, 102 | "l-2e3v": LabeledArcList{ 103 | NewLabeledArc(1, 2, "foo"), 104 | NewLabeledArc(2, 3, "bar"), 105 | }, 106 | "d-2e3v": DataArcList{ 107 | NewDataArc(1, 2, "foo"), 108 | NewDataArc(2, 3, struct{ a int }{a: 2}), 109 | }, 110 | } 111 | 112 | ///////////////////////////////////////////////////////////////////// 113 | // 114 | // HELPERS 115 | // 116 | ///////////////////////////////////////////////////////////////////// 117 | 118 | // Hook gocheck into the go test runner 119 | func TestHookup(t *testing.T) { TestingT(t) } 120 | 121 | // Returns an arc with the directionality swapped. 122 | func Swap(a Arc) Arc { 123 | return NewArc(a.Target(), a.Source()) 124 | } 125 | 126 | func gdebug(g Graph, args ...interface{}) { 127 | fmt.Println("DEBUG: graph type", reflect.New(reflect.Indirect(reflect.ValueOf(g)).Type())) 128 | pretty.Print(args...) 129 | } 130 | 131 | ///////////////////////////////////////////////////////////////////// 132 | // 133 | // SUITE SETUP 134 | // 135 | ///////////////////////////////////////////////////////////////////// 136 | 137 | type graphFactory func(GraphSpec) Graph 138 | 139 | func SetUpTestsFromSpec(gp GraphProperties, fn graphFactory) bool { 140 | var directed bool 141 | 142 | g := fn(GraphSpec{Props: gp}) 143 | 144 | fact := func(gs GraphSource) Graph { 145 | return fn(GraphSpec{Props: gp, Source: gs}) 146 | } 147 | 148 | if _, ok := g.(Digraph); ok { 149 | directed = true 150 | Suite(&DigraphSuite{Factory: fact}) 151 | } 152 | 153 | // Set up the basic Graph suite unconditionally 154 | Suite(&GraphSuite{fact, directed}) 155 | 156 | if _, ok := g.(SimpleGraph); ok { 157 | Suite(&SimpleGraphSuite{fact, directed}) 158 | } 159 | 160 | if _, ok := g.(VertexSetMutator); ok { 161 | Suite(&VertexSetMutatorSuite{fact}) 162 | } 163 | 164 | if _, ok := g.(EdgeSetMutator); ok { 165 | Suite(&EdgeSetMutatorSuite{fact}) 166 | } 167 | 168 | if _, ok := g.(ArcSetMutator); ok { 169 | Suite(&ArcSetMutatorSuite{fact}) 170 | } 171 | 172 | if _, ok := g.(WeightedGraph); ok { 173 | wfact := func(gs GraphSource) WeightedGraph { 174 | return fact(gs).(WeightedGraph) 175 | } 176 | 177 | Suite(&WeightedGraphSuite{wfact}) 178 | 179 | if _, ok := g.(WeightedDigraph); ok { 180 | Suite(&WeightedDigraphSuite{wfact}) 181 | } 182 | if _, ok := g.(WeightedEdgeSetMutator); ok { 183 | Suite(&WeightedEdgeSetMutatorSuite{wfact}) 184 | } 185 | if _, ok := g.(WeightedArcSetMutator); ok { 186 | Suite(&WeightedArcSetMutatorSuite{wfact}) 187 | } 188 | } 189 | 190 | if _, ok := g.(LabeledGraph); ok { 191 | wfact := func(gs GraphSource) LabeledGraph { 192 | return fact(gs).(LabeledGraph) 193 | } 194 | 195 | Suite(&LabeledGraphSuite{wfact}) 196 | 197 | if _, ok := g.(LabeledDigraph); ok { 198 | Suite(&LabeledDigraphSuite{wfact}) 199 | } 200 | if _, ok := g.(LabeledEdgeSetMutator); ok { 201 | Suite(&LabeledEdgeSetMutatorSuite{wfact}) 202 | } 203 | if _, ok := g.(LabeledArcSetMutator); ok { 204 | Suite(&LabeledArcSetMutatorSuite{wfact}) 205 | } 206 | } 207 | 208 | if _, ok := g.(DataGraph); ok { 209 | wfact := func(gs GraphSource) DataGraph { 210 | return fact(gs).(DataGraph) 211 | } 212 | 213 | Suite(&DataGraphSuite{wfact}) 214 | 215 | if _, ok := g.(DataDigraph); ok { 216 | Suite(&DataDigraphSuite{wfact}) 217 | } 218 | if _, ok := g.(DataEdgeSetMutator); ok { 219 | Suite(&DataEdgeSetMutatorSuite{wfact}) 220 | } 221 | if _, ok := g.(DataArcSetMutator); ok { 222 | Suite(&DataArcSetMutatorSuite{wfact}) 223 | } 224 | } 225 | 226 | return true 227 | } 228 | -------------------------------------------------------------------------------- /spec/literal.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | . "github.com/sdboyer/gogl" 5 | ) 6 | 7 | // Define a graph literal fixture for testing here. 8 | // The literal has two edges and four vertices; one vertex is an isolate. 9 | // 10 | // bool state indicates whether using a transpose or not. 11 | type GraphLiteralFixture bool 12 | 13 | func (g GraphLiteralFixture) Vertices(f VertexStep) { 14 | vl := []Vertex{"foo", "bar", "baz", "isolate"} 15 | for _, v := range vl { 16 | if f(v) { 17 | return 18 | } 19 | } 20 | } 21 | 22 | func (g GraphLiteralFixture) Edges(f EdgeStep) { 23 | var el []Edge 24 | if g { 25 | el = []Edge{ 26 | NewEdge("foo", "bar"), 27 | NewEdge("bar", "baz"), 28 | } 29 | } else { 30 | el = []Edge{ 31 | NewEdge("bar", "foo"), 32 | NewEdge("baz", "bar"), 33 | } 34 | } 35 | 36 | for _, e := range el { 37 | if f(e) { 38 | return 39 | } 40 | } 41 | } 42 | 43 | func (g GraphLiteralFixture) Arcs(f ArcStep) { 44 | var al []Arc 45 | if g { 46 | al = []Arc{ 47 | NewArc("foo", "bar"), 48 | NewArc("bar", "baz"), 49 | } 50 | } else { 51 | al = []Arc{ 52 | NewArc("bar", "foo"), 53 | NewArc("baz", "bar"), 54 | } 55 | } 56 | 57 | for _, e := range al { 58 | if f(e) { 59 | return 60 | } 61 | } 62 | } 63 | 64 | func (g GraphLiteralFixture) IncidentTo(v Vertex, f EdgeStep) { 65 | if g { 66 | switch v { 67 | case "foo": 68 | f(NewEdge("foo", "bar")) 69 | case "bar": 70 | terminate := f(NewEdge("foo", "bar")) 71 | if !terminate { 72 | f(NewEdge("bar", "baz")) 73 | } 74 | case "baz": 75 | f(NewEdge("bar", "baz")) 76 | default: 77 | } 78 | } else { 79 | switch v { 80 | case "foo": 81 | f(NewEdge("bar", "foo")) 82 | case "bar": 83 | terminate := f(NewEdge("bar", "foo")) 84 | if !terminate { 85 | f(NewEdge("baz", "bar")) 86 | } 87 | case "baz": 88 | f(NewEdge("baz", "bar")) 89 | default: 90 | } 91 | } 92 | } 93 | 94 | func (g GraphLiteralFixture) ArcsFrom(v Vertex, f ArcStep) { 95 | if g { 96 | switch v { 97 | case "foo": 98 | f(NewArc("foo", "bar")) 99 | case "bar": 100 | f(NewArc("bar", "baz")) 101 | default: 102 | } 103 | } else { 104 | switch v { 105 | case "bar": 106 | f(NewArc("bar", "foo")) 107 | case "baz": 108 | f(NewArc("baz", "bar")) 109 | default: 110 | } 111 | } 112 | } 113 | 114 | func (g GraphLiteralFixture) ArcsTo(v Vertex, f ArcStep) { 115 | if g { 116 | switch v { 117 | case "bar": 118 | f(NewArc("foo", "bar")) 119 | case "baz": 120 | f(NewArc("bar", "baz")) 121 | default: 122 | } 123 | } else { 124 | switch v { 125 | case "foo": 126 | f(NewArc("bar", "foo")) 127 | case "bar": 128 | f(NewArc("baz", "bar")) 129 | default: 130 | } 131 | } 132 | } 133 | 134 | func (g GraphLiteralFixture) PredecessorsOf(v Vertex, f VertexStep) { 135 | if g { 136 | switch v { 137 | case "bar": 138 | f("foo") 139 | case "baz": 140 | f("bar") 141 | default: 142 | } 143 | } else { 144 | switch v { 145 | case "foo": 146 | f("bar") 147 | case "bar": 148 | f("baz") 149 | default: 150 | } 151 | } 152 | } 153 | func (g GraphLiteralFixture) SuccessorsOf(v Vertex, f VertexStep) { 154 | if g { 155 | switch v { 156 | case "foo": 157 | f("bar") 158 | case "bar": 159 | f("baz") 160 | default: 161 | } 162 | } else { 163 | switch v { 164 | case "bar": 165 | f("foo") 166 | case "baz": 167 | f("bar") 168 | default: 169 | } 170 | } 171 | } 172 | 173 | func (g GraphLiteralFixture) AdjacentTo(v Vertex, f VertexStep) { 174 | switch v { 175 | case "foo": 176 | f("bar") 177 | case "bar": 178 | terminate := f("foo") 179 | if !terminate { 180 | f("baz") 181 | } 182 | case "baz": 183 | f("bar") 184 | default: 185 | } 186 | } 187 | 188 | func (g GraphLiteralFixture) HasVertex(v Vertex) bool { 189 | switch v { 190 | case "foo", "bar", "baz", "isolate": 191 | return true 192 | default: 193 | return false 194 | } 195 | } 196 | 197 | func (g GraphLiteralFixture) InDegreeOf(v Vertex) (degree int, exists bool) { 198 | if g { 199 | switch v { 200 | case "foo": 201 | return 0, true 202 | case "bar": 203 | return 1, true 204 | case "baz": 205 | return 1, true 206 | case "isolate": 207 | return 0, true 208 | default: 209 | return 0, false 210 | } 211 | } else { 212 | switch v { 213 | case "foo": 214 | return 1, true 215 | case "bar": 216 | return 1, true 217 | case "baz": 218 | return 0, true 219 | case "isolate": 220 | return 0, true 221 | default: 222 | return 0, false 223 | } 224 | } 225 | } 226 | 227 | func (g GraphLiteralFixture) OutDegreeOf(v Vertex) (degree int, exists bool) { 228 | if g { 229 | switch v { 230 | case "foo": 231 | return 1, true 232 | case "bar": 233 | return 1, true 234 | case "baz": 235 | return 0, true 236 | case "isolate": 237 | return 0, true 238 | default: 239 | return 0, false 240 | } 241 | 242 | } else { 243 | switch v { 244 | case "foo": 245 | return 0, true 246 | case "bar": 247 | return 1, true 248 | case "baz": 249 | return 1, true 250 | case "isolate": 251 | return 0, true 252 | default: 253 | return 0, false 254 | } 255 | } 256 | } 257 | 258 | func (g GraphLiteralFixture) DegreeOf(v Vertex) (degree int, exists bool) { 259 | switch v { 260 | case "foo": 261 | return 1, true 262 | case "bar": 263 | return 2, true 264 | case "baz": 265 | return 1, true 266 | case "isolate": 267 | return 0, true 268 | default: 269 | return 0, false 270 | } 271 | } 272 | 273 | func (g GraphLiteralFixture) HasEdge(e Edge) bool { 274 | u, v := e.Both() 275 | 276 | switch u { 277 | case "foo": 278 | return v == "bar" 279 | case "bar": 280 | return v == "baz" || v == "foo" 281 | case "baz": 282 | return v == "bar" 283 | default: 284 | return false 285 | } 286 | } 287 | 288 | func (g GraphLiteralFixture) HasArc(a Arc) bool { 289 | u, v := a.Both() 290 | 291 | if g { 292 | switch u { 293 | case "foo": 294 | return v == "bar" 295 | case "bar": 296 | return v == "baz" 297 | default: 298 | return false 299 | } 300 | } else { 301 | switch u { 302 | case "bar": 303 | return v == "foo" 304 | case "baz": 305 | return v == "bar" 306 | default: 307 | return false 308 | } 309 | } 310 | } 311 | 312 | func (g GraphLiteralFixture) Density() float64 { 313 | return 2 / 12 // 2 edges of maximum 12 in a 4-vertex digraph 314 | } 315 | 316 | func (g GraphLiteralFixture) Transpose() Digraph { 317 | return GraphLiteralFixture(!g) 318 | } 319 | 320 | func (g GraphLiteralFixture) Size() int { 321 | return 2 322 | } 323 | 324 | func (g GraphLiteralFixture) Order() int { 325 | return 4 326 | } 327 | -------------------------------------------------------------------------------- /spec/suite_basicmut.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | ) 9 | 10 | /* Suites for mutable graph methods */ 11 | 12 | type VertexSetMutatorSuite struct { 13 | Factory func(GraphSource) Graph 14 | } 15 | 16 | func (s *VertexSetMutatorSuite) SuiteLabel() string { 17 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 18 | } 19 | 20 | func (s *VertexSetMutatorSuite) TestEnsureVertex(c *C) { 21 | g := s.Factory(NullGraph) 22 | m := g.(VertexSetMutator) 23 | 24 | m.EnsureVertex("foo") 25 | c.Assert(g.HasVertex("foo"), Equals, true) 26 | } 27 | 28 | func (s *VertexSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 29 | g := s.Factory(NullGraph) 30 | m := g.(VertexSetMutator) 31 | 32 | m.EnsureVertex() 33 | c.Assert(Order(g), Equals, 0) 34 | 35 | m.RemoveVertex() 36 | c.Assert(Order(g), Equals, 0) 37 | } 38 | 39 | func (s *VertexSetMutatorSuite) TestMultiEnsureVertex(c *C) { 40 | g := s.Factory(NullGraph) 41 | m := g.(VertexSetMutator) 42 | 43 | m.EnsureVertex("bar", "baz") 44 | c.Assert(g.HasVertex("bar"), Equals, true) 45 | c.Assert(g.HasVertex("baz"), Equals, true) 46 | } 47 | 48 | func (s *VertexSetMutatorSuite) TestRemoveVertex(c *C) { 49 | g := s.Factory(NullGraph) 50 | m := g.(VertexSetMutator) 51 | 52 | m.EnsureVertex("bar", "baz") 53 | m.RemoveVertex("bar") 54 | c.Assert(g.HasVertex("bar"), Equals, false) 55 | } 56 | 57 | func (s *VertexSetMutatorSuite) TestMultiRemoveVertex(c *C) { 58 | g := s.Factory(NullGraph) 59 | m := g.(VertexSetMutator) 60 | 61 | m.EnsureVertex("bar", "baz") 62 | m.RemoveVertex("bar", "baz") 63 | c.Assert(g.HasVertex("bar"), Equals, false) 64 | c.Assert(g.HasVertex("baz"), Equals, false) 65 | } 66 | 67 | type EdgeSetMutatorSuite struct { 68 | Factory func(GraphSource) Graph 69 | } 70 | 71 | func (s *EdgeSetMutatorSuite) SuiteLabel() string { 72 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 73 | } 74 | 75 | func (s *EdgeSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 76 | g := s.Factory(NullGraph) 77 | m := g.(EdgeSetMutator) 78 | 79 | m.AddEdges() 80 | c.Assert(Order(g), Equals, 0) 81 | c.Assert(Size(g), Equals, 0) 82 | 83 | m.RemoveEdges() 84 | c.Assert(Order(g), Equals, 0) 85 | c.Assert(Size(g), Equals, 0) 86 | } 87 | 88 | func (s *EdgeSetMutatorSuite) TestAddRemoveHasEdge(c *C) { 89 | g := s.Factory(NullGraph) 90 | m := g.(EdgeSetMutator) 91 | 92 | m.AddEdges(NewEdge(1, 2)) 93 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, true) 94 | c.Assert(g.HasEdge(NewEdge(2, 1)), Equals, true) 95 | 96 | // Now test removal 97 | m.RemoveEdges(NewEdge(1, 2)) 98 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 99 | c.Assert(g.HasEdge(NewEdge(2, 1)), Equals, false) 100 | } 101 | 102 | func (s *EdgeSetMutatorSuite) TestMultiAddRemoveHasEdge(c *C) { 103 | g := s.Factory(NullGraph) 104 | m := g.(EdgeSetMutator) 105 | 106 | m.AddEdges(NewEdge(1, 2), NewEdge(2, 3)) 107 | 108 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, true) 109 | c.Assert(g.HasEdge(NewEdge(2, 3)), Equals, true) 110 | c.Assert(g.HasEdge(NewEdge(2, 1)), Equals, true) 111 | c.Assert(g.HasEdge(NewEdge(3, 2)), Equals, true) 112 | 113 | // Now test removal 114 | m.RemoveEdges(NewEdge(1, 2), NewEdge(2, 3)) 115 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 116 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 117 | c.Assert(g.HasEdge(NewEdge(2, 3)), Equals, false) 118 | c.Assert(g.HasEdge(NewEdge(2, 3)), Equals, false) 119 | } 120 | 121 | // Checks to ensure that removal works for both in-edges and out-edges. 122 | func (s *EdgeSetMutatorSuite) TestVertexRemovalAlsoRemovesConnectedEdges(c *C) { 123 | g := s.Factory(NullGraph) 124 | m := g.(EdgeSetMutator) 125 | 126 | if v, ok := g.(VertexSetMutator); ok { // Almost always gonna be the case that we have both 127 | m.AddEdges(NewEdge(1, 2), NewEdge(2, 3), NewEdge(4, 1)) 128 | v.RemoveVertex(1) 129 | 130 | c.Assert(Size(g), Equals, 1) 131 | } 132 | } 133 | 134 | func (s *ArcSetMutatorSuite) SuiteLabel() string { 135 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 136 | } 137 | 138 | type ArcSetMutatorSuite struct { 139 | Factory func(GraphSource) Graph 140 | } 141 | 142 | func (s *ArcSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 143 | g := s.Factory(NullGraph) 144 | m := g.(ArcSetMutator) 145 | 146 | m.AddArcs() 147 | c.Assert(Order(g), Equals, 0) 148 | c.Assert(Size(g), Equals, 0) 149 | 150 | m.RemoveArcs() 151 | c.Assert(Order(g), Equals, 0) 152 | c.Assert(Size(g), Equals, 0) 153 | } 154 | 155 | func (s *ArcSetMutatorSuite) TestAddRemoveHasArc(c *C) { 156 | g := s.Factory(NullGraph).(Digraph) 157 | m := g.(ArcSetMutator) 158 | 159 | m.AddArcs(NewArc(1, 2)) 160 | c.Assert(g.HasArc(NewArc(1, 2)), Equals, true) 161 | c.Assert(g.HasArc(NewArc(2, 1)), Equals, false) 162 | 163 | // Now test removal 164 | m.RemoveArcs(NewArc(1, 2)) 165 | c.Assert(g.HasArc(NewArc(1, 2)), Equals, false) 166 | c.Assert(g.HasArc(NewArc(2, 1)), Equals, false) 167 | } 168 | 169 | func (s *ArcSetMutatorSuite) TestMultiAddRemoveHasArc(c *C) { 170 | g := s.Factory(NullGraph).(Digraph) 171 | m := g.(ArcSetMutator) 172 | 173 | m.AddArcs(NewArc(1, 2), NewArc(2, 3)) 174 | 175 | c.Assert(g.HasArc(NewArc(1, 2)), Equals, true) 176 | c.Assert(g.HasArc(NewArc(2, 3)), Equals, true) 177 | c.Assert(g.HasArc(NewArc(2, 1)), Equals, false) 178 | c.Assert(g.HasArc(NewArc(3, 2)), Equals, false) 179 | 180 | // Now test removal 181 | m.RemoveArcs(NewArc(1, 2), NewArc(2, 3)) 182 | c.Assert(g.HasArc(NewArc(1, 2)), Equals, false) 183 | c.Assert(g.HasArc(NewArc(1, 2)), Equals, false) 184 | c.Assert(g.HasArc(NewArc(2, 3)), Equals, false) 185 | c.Assert(g.HasArc(NewArc(2, 3)), Equals, false) 186 | } 187 | 188 | // Checks to ensure that removal works for both in-edges and out-edges. 189 | func (s *ArcSetMutatorSuite) TestVertexRemovalAlsoRemovesConnectedArcs(c *C) { 190 | g := s.Factory(NullGraph) 191 | m := g.(ArcSetMutator) 192 | 193 | if v, ok := g.(VertexSetMutator); ok { 194 | // Almost always gonona be the case that we have both 195 | m.AddArcs(NewArc(1, 2), NewArc(2, 3), NewArc(4, 1)) 196 | v.RemoveVertex(1) 197 | 198 | c.Assert(Size(g), Equals, 1) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /spec/suite_count.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | ) 9 | 10 | /* Counting suites - tests for Size() and Order() */ 11 | 12 | type OrderSuite struct { 13 | Factory func(GraphSource) Graph 14 | Directed bool 15 | } 16 | 17 | func (s *OrderSuite) SuiteLabel() string { 18 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 19 | } 20 | 21 | func (s *OrderSuite) TestOrder(c *C) { 22 | c.Assert(s.Factory(NullGraph).(VertexCounter).Order(), Equals, 0) 23 | c.Assert(s.Factory(GraphFixtures["2e3v"]).(VertexCounter).Order(), Equals, 3) 24 | } 25 | 26 | type SizeSuite struct { 27 | Factory func(GraphSource) Graph 28 | Directed bool 29 | } 30 | 31 | func (s *SizeSuite) SuiteLabel() string { 32 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 33 | } 34 | 35 | func (s *SizeSuite) TestSize(c *C) { 36 | c.Assert(s.Factory(NullGraph).(EdgeCounter).Size(), Equals, 0) 37 | c.Assert(s.Factory(GraphFixtures["2e3v"]).(EdgeCounter).Size(), Equals, 2) 38 | } 39 | -------------------------------------------------------------------------------- /spec/suite_data.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | ) 9 | 10 | /* DataGraphSuite - tests for data graphs */ 11 | 12 | type DataGraphSuite struct { 13 | Factory func(GraphSource) DataGraph 14 | } 15 | 16 | func (s *DataGraphSuite) SuiteLabel() string { 17 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 18 | } 19 | 20 | func (s *DataGraphSuite) TestEdges(c *C) { 21 | // This method is not redundant with the base Graph suite as it ensures that the edges 22 | // provided by the Edges() iterator actually do implement DataEdge. 23 | g := s.Factory(GraphFixtures["d-2e3v"]) 24 | 25 | var we DataEdge 26 | g.Edges(func(e Edge) (terminate bool) { 27 | c.Assert(e, Implements, &we) 28 | return 29 | }) 30 | } 31 | 32 | func (s *DataGraphSuite) TestHasDataEdge(c *C) { 33 | g := s.Factory(GraphFixtures["d-2e3v"]) 34 | 35 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "foo")), Equals, true) 36 | c.Assert(g.HasDataEdge(NewDataEdge(2, 1, "foo")), Equals, true) // both directions work 37 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "qux")), Equals, false) // wrong data 38 | } 39 | 40 | type DataDigraphSuite struct { 41 | Factory func(GraphSource) DataGraph 42 | } 43 | 44 | func (s *DataDigraphSuite) SuiteLabel() string { 45 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 46 | } 47 | 48 | func (s *DataDigraphSuite) TestArcSubtypeImplementation(c *C) { 49 | // This method is not redundant with the base Graph suite as it ensures that the edges 50 | // provided by the Arcs() iterator actually do implement DataArc. 51 | g := s.Factory(GraphFixtures["d-2e3v"]).(DataDigraph) 52 | 53 | var hit int // just internal safety check to ensure the fixture is good and hits 54 | var wa DataArc 55 | g.Arcs(func(e Arc) (terminate bool) { 56 | hit++ 57 | c.Assert(e, Implements, &wa) 58 | return 59 | }) 60 | 61 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 62 | hit++ 63 | c.Assert(e, Implements, &wa) 64 | return 65 | }) 66 | 67 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 68 | hit++ 69 | c.Assert(e, Implements, &wa) 70 | return 71 | }) 72 | 73 | c.Assert(hit, Equals, 4) 74 | } 75 | 76 | /* DataEdgeSetMutatorSuite - tests for mutable data graphs */ 77 | 78 | type DataEdgeSetMutatorSuite struct { 79 | Factory func(GraphSource) DataGraph 80 | } 81 | 82 | func (s *DataEdgeSetMutatorSuite) SuiteLabel() string { 83 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 84 | } 85 | 86 | func (s *DataEdgeSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 87 | g := s.Factory(NullGraph) 88 | m := g.(DataEdgeSetMutator) 89 | 90 | m.AddEdges() 91 | c.Assert(Order(g), Equals, 0) 92 | c.Assert(Size(g), Equals, 0) 93 | 94 | m.RemoveEdges() 95 | c.Assert(Order(g), Equals, 0) 96 | c.Assert(Size(g), Equals, 0) 97 | } 98 | 99 | func (s *DataEdgeSetMutatorSuite) TestAddRemoveEdge(c *C) { 100 | g := s.Factory(NullGraph) 101 | m := g.(DataEdgeSetMutator) 102 | 103 | m.AddEdges(NewDataEdge(1, 2, "foo")) 104 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "foo")), Equals, true) 105 | 106 | // Now test removal 107 | m.RemoveEdges(NewDataEdge(1, 2, "foo")) 108 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 109 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "foo")), Equals, false) 110 | } 111 | 112 | func (s *DataEdgeSetMutatorSuite) TestMultiAddRemoveEdge(c *C) { 113 | g := s.Factory(NullGraph) 114 | m := g.(DataEdgeSetMutator) 115 | 116 | m.AddEdges(NewDataEdge(1, 2, "foo"), NewDataEdge(2, 3, "bar")) 117 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "foo")), Equals, true) 118 | c.Assert(g.HasDataEdge(NewDataEdge(2, 3, "bar")), Equals, true) 119 | 120 | // Now test removal 121 | m.RemoveEdges(NewDataEdge(1, 2, "foo"), NewDataEdge(2, 3, "bar")) 122 | c.Assert(g.HasDataEdge(NewDataEdge(1, 2, "foo")), Equals, false) 123 | c.Assert(g.HasDataEdge(NewDataEdge(2, 3, "bar")), Equals, false) 124 | } 125 | 126 | /* DataArcSetMutatorSuite - tests for mutable data graphs */ 127 | 128 | type DataArcSetMutatorSuite struct { 129 | Factory func(GraphSource) DataGraph 130 | } 131 | 132 | func (s *DataArcSetMutatorSuite) SuiteLabel() string { 133 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 134 | } 135 | 136 | func (s *DataArcSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 137 | g := s.Factory(NullGraph).(DataDigraph) 138 | m := g.(DataArcSetMutator) 139 | 140 | m.AddArcs() 141 | c.Assert(Order(g), Equals, 0) 142 | c.Assert(Size(g), Equals, 0) 143 | 144 | m.RemoveArcs() 145 | c.Assert(Order(g), Equals, 0) 146 | c.Assert(Size(g), Equals, 0) 147 | } 148 | 149 | func (s *DataArcSetMutatorSuite) TestAddRemoveHasArc(c *C) { 150 | g := s.Factory(NullGraph).(DataDigraph) 151 | m := g.(DataArcSetMutator) 152 | 153 | m.AddArcs(NewDataArc(1, 2, "foo")) 154 | c.Assert(g.HasDataArc(NewDataArc(1, 2, "foo")), Equals, true) 155 | c.Assert(g.HasDataArc(NewDataArc(1, 2, "bar")), Equals, false) // wrong data 156 | 157 | // Now test removal 158 | m.RemoveArcs(NewDataArc(1, 2, "foo")) 159 | c.Assert(g.HasDataArc(NewDataArc(1, 2, "foo")), Equals, false) 160 | } 161 | 162 | func (s *DataArcSetMutatorSuite) TestMultiAddRemoveHasArc(c *C) { 163 | g := s.Factory(NullGraph).(DataDigraph) 164 | m := g.(DataArcSetMutator) 165 | 166 | m.AddArcs(NewDataArc(1, 2, "foo"), NewDataArc(2, 3, "bar")) 167 | c.Assert(g.HasDataArc(NewDataArc(1, 2, "foo")), Equals, true) 168 | c.Assert(g.HasDataArc(NewDataArc(2, 3, "bar")), Equals, true) 169 | 170 | m.RemoveArcs(NewDataArc(1, 2, "foo"), NewDataArc(2, 3, "bar")) 171 | c.Assert(g.HasDataArc(NewDataArc(1, 2, "foo")), Equals, false) 172 | c.Assert(g.HasDataArc(NewDataArc(2, 3, "bar")), Equals, false) 173 | } 174 | -------------------------------------------------------------------------------- /spec/suite_digraph.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | "gopkg.in/fatih/set.v0" 9 | ) 10 | 11 | /* DigraphSuite - tests for directed graph methods */ 12 | 13 | type DigraphSuite struct { 14 | Factory func(GraphSource) Graph 15 | } 16 | 17 | func (s *DigraphSuite) SuiteLabel() string { 18 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 19 | } 20 | 21 | func (s *DigraphSuite) TestTranspose(c *C) { 22 | g := s.Factory(GraphFixtures["2e3v"]).(Digraph) 23 | 24 | g2 := g.Transpose() 25 | 26 | c.Assert(g2.HasArc(Swap(GraphFixtures["2e3v"].(ArcList)[0])), Equals, true) 27 | c.Assert(g2.HasArc(Swap(GraphFixtures["2e3v"].(ArcList)[1])), Equals, true) 28 | 29 | c.Assert(g2.HasArc(GraphFixtures["2e3v"].(ArcList)[0]), Equals, false) 30 | c.Assert(g2.HasArc(GraphFixtures["2e3v"].(ArcList)[1]), Equals, false) 31 | } 32 | 33 | func (s *DigraphSuite) TestOutDegreeOf(c *C) { 34 | g := s.Factory(GraphFixtures["3e5v1i"]).(Digraph) 35 | 36 | count, exists := g.OutDegreeOf("foo") 37 | c.Assert(exists, Equals, true) 38 | c.Assert(count, Equals, 2) 39 | 40 | count, exists = g.OutDegreeOf("bar") 41 | c.Assert(exists, Equals, true) 42 | c.Assert(count, Equals, 1) 43 | 44 | count, exists = g.OutDegreeOf("baz") 45 | c.Assert(exists, Equals, true) 46 | c.Assert(count, Equals, 0) 47 | 48 | count, exists = g.OutDegreeOf("qux") 49 | c.Assert(exists, Equals, true) 50 | c.Assert(count, Equals, 0) 51 | 52 | count, exists = g.DegreeOf("isolate") 53 | c.Assert(exists, Equals, true) 54 | c.Assert(count, Equals, 0) 55 | 56 | count, exists = g.OutDegreeOf("missing") 57 | c.Assert(exists, Equals, false) 58 | c.Assert(count, Equals, 0) 59 | } 60 | 61 | func (s *DigraphSuite) TestInDegreeOf(c *C) { 62 | g := s.Factory(GraphFixtures["3e5v1i"]).(Digraph) 63 | 64 | count, exists := g.InDegreeOf("foo") 65 | c.Assert(exists, Equals, true) 66 | c.Assert(count, Equals, 0) 67 | 68 | count, exists = g.InDegreeOf("bar") 69 | c.Assert(exists, Equals, true) 70 | c.Assert(count, Equals, 1) 71 | 72 | count, exists = g.InDegreeOf("baz") 73 | c.Assert(exists, Equals, true) 74 | c.Assert(count, Equals, 1) 75 | 76 | count, exists = g.InDegreeOf("qux") 77 | c.Assert(exists, Equals, true) 78 | c.Assert(count, Equals, 1) 79 | 80 | count, exists = g.DegreeOf("isolate") 81 | c.Assert(exists, Equals, true) 82 | c.Assert(count, Equals, 0) 83 | 84 | count, exists = g.InDegreeOf("missing") 85 | c.Assert(exists, Equals, false) 86 | c.Assert(count, Equals, 0) 87 | } 88 | 89 | func (s *DigraphSuite) TestArcsTo(c *C) { 90 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 91 | 92 | eset := set.New(set.NonThreadSafe) 93 | var hit int 94 | g.ArcsTo("foo", func(e Arc) (terminate bool) { 95 | c.Error("Vertex 'foo' should have no in-edges") 96 | c.FailNow() 97 | return 98 | }) 99 | 100 | g.ArcsTo("bar", func(e Arc) (terminate bool) { 101 | // A more specific edge type may be passed, but in this test we care only about the base 102 | eset.Add(NewArc(e.Both())) 103 | hit++ 104 | return 105 | }) 106 | 107 | c.Assert(hit, Equals, 2) 108 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]), Equals, true) 109 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]), Equals, false) 110 | c.Assert(eset.Has(NewArc("qux", "bar")), Equals, true) 111 | } 112 | 113 | func (s *DigraphSuite) TestArcsToTermination(c *C) { 114 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 115 | 116 | var hit int 117 | g.ArcsTo("baz", func(e Arc) (terminate bool) { 118 | hit++ 119 | return true 120 | }) 121 | 122 | c.Assert(hit, Equals, 1) 123 | } 124 | 125 | func (s *DigraphSuite) TestPredecessorsOf(c *C) { 126 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 127 | 128 | eset := set.New(set.NonThreadSafe) 129 | g.PredecessorsOf("foo", func(v Vertex) (terminate bool) { 130 | c.Error("Vertex 'foo' should have no predecessors") 131 | c.FailNow() 132 | return 133 | }) 134 | 135 | g.PredecessorsOf("bar", func(v Vertex) (terminate bool) { 136 | eset.Add(v) 137 | return 138 | }) 139 | 140 | c.Assert(eset.Size(), Equals, 2) 141 | c.Assert(eset.Has("foo"), Equals, true) 142 | c.Assert(eset.Has("qux"), Equals, true) 143 | 144 | } 145 | 146 | func (s *DigraphSuite) TestPredecessorsOfTermination(c *C) { 147 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 148 | 149 | var hit int 150 | g.PredecessorsOf("baz", func(v Vertex) (terminate bool) { 151 | hit++ 152 | return true 153 | }) 154 | 155 | c.Assert(hit, Equals, 1) 156 | } 157 | 158 | func (s *DigraphSuite) TestArcsFrom(c *C) { 159 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 160 | 161 | eset := set.New(set.NonThreadSafe) 162 | var hit int 163 | g.ArcsFrom("baz", func(e Arc) (terminate bool) { 164 | c.Error("Vertex 'baz' should have no out-edges") 165 | c.FailNow() 166 | return 167 | }) 168 | 169 | g.ArcsFrom("foo", func(e Arc) (terminate bool) { 170 | // A more specific edge type may be passed, but in this test we care only about the base 171 | eset.Add(NewArc(e.Both())) 172 | hit++ 173 | return 174 | }) 175 | 176 | c.Assert(hit, Equals, 2) 177 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]), Equals, true) 178 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]), Equals, false) 179 | c.Assert(eset.Has(NewArc("foo", "qux")), Equals, true) 180 | } 181 | 182 | func (s *DigraphSuite) TestArcsFromTermination(c *C) { 183 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 184 | 185 | var hit int 186 | g.ArcsFrom("foo", func(e Arc) (terminate bool) { 187 | hit++ 188 | return true 189 | }) 190 | 191 | c.Assert(hit, Equals, 1) 192 | } 193 | 194 | func (s *DigraphSuite) TestSuccessorsOf(c *C) { 195 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 196 | 197 | eset := set.New(set.NonThreadSafe) 198 | g.SuccessorsOf("baz", func(v Vertex) (terminate bool) { 199 | c.Error("Vertex 'foo' should have no successors") 200 | c.FailNow() 201 | return 202 | }) 203 | 204 | g.SuccessorsOf("foo", func(v Vertex) (terminate bool) { 205 | eset.Add(v) 206 | return 207 | }) 208 | 209 | c.Assert(eset.Size(), Equals, 2) 210 | c.Assert(eset.Has("qux"), Equals, true) 211 | c.Assert(eset.Has("bar"), Equals, true) 212 | 213 | } 214 | 215 | func (s *DigraphSuite) TestSuccessorsOfTermination(c *C) { 216 | g := s.Factory(GraphFixtures["arctest"]).(Digraph) 217 | 218 | var hit int 219 | g.SuccessorsOf("foo", func(v Vertex) (terminate bool) { 220 | hit++ 221 | return true 222 | }) 223 | 224 | c.Assert(hit, Equals, 1) 225 | } 226 | -------------------------------------------------------------------------------- /spec/suite_graph.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | "gopkg.in/fatih/set.v0" 9 | ) 10 | 11 | /* GraphSuite - tests for non-mutable graph methods */ 12 | 13 | type GraphSuite struct { 14 | Factory func(GraphSource) Graph 15 | Directed bool 16 | } 17 | 18 | func (s *GraphSuite) SuiteLabel() string { 19 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 20 | } 21 | 22 | func (s *GraphSuite) TestHasVertex(c *C) { 23 | g := s.Factory(GraphFixtures["2e3v"]) 24 | c.Assert(g.HasVertex("qux"), Equals, false) 25 | c.Assert(g.HasVertex("foo"), Equals, true) 26 | } 27 | 28 | func (s *GraphSuite) TestHasEdge(c *C) { 29 | g := s.Factory(GraphFixtures["2e3v"]) 30 | // Testing match with minimum possible specificity here 31 | c.Assert(g.HasEdge(GraphFixtures["2e3v"].(ArcList)[0]), Equals, true) 32 | c.Assert(g.HasEdge(NewEdge("qux", "quark")), Equals, false) 33 | } 34 | 35 | func (s *GraphSuite) TestVertices(c *C) { 36 | g := s.Factory(GraphFixtures["2e3v"]) 37 | 38 | vset := set.New(set.NonThreadSafe) 39 | var hit int 40 | g.Vertices(func(v Vertex) (terminate bool) { 41 | hit++ 42 | vset.Add(v) 43 | return 44 | }) 45 | 46 | c.Assert(vset.Has("foo"), Equals, true) 47 | c.Assert(vset.Has("bar"), Equals, true) 48 | c.Assert(vset.Has("baz"), Equals, true) 49 | c.Assert(hit, Equals, 3) 50 | } 51 | 52 | func (s *GraphSuite) TestVerticesTermination(c *C) { 53 | g := s.Factory(GraphFixtures["2e3v"]) 54 | 55 | var hit int 56 | g.Vertices(func(v Vertex) bool { 57 | hit++ 58 | return true 59 | }) 60 | 61 | c.Assert(hit, Equals, 1) 62 | } 63 | 64 | func (s *GraphSuite) TestEdges(c *C) { 65 | g := s.Factory(GraphFixtures["2e3v"]) 66 | 67 | var hit int 68 | g.Edges(func(e Edge) (terminate bool) { 69 | hit++ 70 | return 71 | }) 72 | 73 | c.Assert(hit, Equals, 2) 74 | } 75 | 76 | func (s *GraphSuite) TestEdgesTermination(c *C) { 77 | g := s.Factory(GraphFixtures["2e3v"]) 78 | 79 | var hit int 80 | g.Edges(func(e Edge) bool { 81 | hit++ 82 | return true 83 | }) 84 | 85 | c.Assert(hit, Equals, 1) 86 | } 87 | 88 | func (s *GraphSuite) TestAdjacentTo(c *C) { 89 | g := s.Factory(GraphFixtures["2e3v"]) 90 | 91 | vset := set.New(set.NonThreadSafe) 92 | var hit int 93 | g.AdjacentTo("bar", func(adj Vertex) (terminate bool) { 94 | hit++ 95 | vset.Add(adj) 96 | return 97 | }) 98 | 99 | c.Assert(vset.Has("foo"), Equals, true) 100 | c.Assert(vset.Has("bar"), Equals, false) 101 | c.Assert(vset.Has("baz"), Equals, true) 102 | c.Assert(hit, Equals, 2) 103 | } 104 | 105 | func (s *GraphSuite) TestAdjacentToTermination(c *C) { 106 | g := s.Factory(GraphFixtures["3e4v"]) 107 | 108 | var hit int 109 | g.AdjacentTo("foo", func(adjacent Vertex) bool { 110 | hit++ 111 | return true 112 | }) 113 | 114 | c.Assert(hit, Equals, 1) 115 | } 116 | 117 | func (s *GraphSuite) TestIncidentTo(c *C) { 118 | g := s.Factory(GraphFixtures["2e3v"]) 119 | 120 | flipset := []Edge{ 121 | Swap(GraphFixtures["2e3v"].(ArcList)[0]), 122 | Swap(GraphFixtures["2e3v"].(ArcList)[1]), 123 | } 124 | 125 | eset := set.New(set.NonThreadSafe) 126 | var hit int 127 | g.IncidentTo("foo", func(e Edge) (terminate bool) { 128 | hit++ 129 | // A more specific edge type may be passed, but in this test we care only about the base 130 | eset.Add(NewArc(e.Both())) 131 | return 132 | }) 133 | 134 | c.Assert(hit, Equals, 1) 135 | if s.Directed { 136 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]), Equals, true) 137 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]), Equals, false) 138 | } else { 139 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]) != eset.Has(flipset[0]), Equals, true) 140 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]) != eset.Has(flipset[1]), Equals, false) 141 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]), Equals, false) 142 | } 143 | 144 | eset = set.New(set.NonThreadSafe) 145 | g.IncidentTo("bar", func(e Edge) (terminate bool) { 146 | hit++ 147 | // A more specific edge type may be passed, but in this test we care only about the base 148 | eset.Add(NewArc(e.Both())) 149 | return 150 | }) 151 | 152 | c.Assert(hit, Equals, 3) 153 | if s.Directed { 154 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]), Equals, true) 155 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]), Equals, true) 156 | } else { 157 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[0]) != eset.Has(flipset[0]), Equals, true) 158 | c.Assert(eset.Has(GraphFixtures["2e3v"].(ArcList)[1]) != eset.Has(flipset[1]), Equals, true) 159 | } 160 | } 161 | 162 | func (s *GraphSuite) TestIncidentToTermination(c *C) { 163 | g := s.Factory(GraphFixtures["2e3v"]) 164 | 165 | var hit int 166 | g.IncidentTo("bar", func(e Edge) (terminate bool) { 167 | hit++ 168 | return true 169 | }) 170 | 171 | c.Assert(hit, Equals, 1) 172 | } 173 | 174 | func (s *GraphSuite) TestDegreeOf(c *C) { 175 | g := s.Factory(GraphFixtures["3e5v1i"]) 176 | 177 | count, exists := g.DegreeOf("foo") 178 | c.Assert(exists, Equals, true) 179 | c.Assert(count, Equals, 2) 180 | 181 | count, exists = g.DegreeOf("bar") 182 | c.Assert(exists, Equals, true) 183 | c.Assert(count, Equals, 2) 184 | 185 | count, exists = g.DegreeOf("baz") 186 | c.Assert(exists, Equals, true) 187 | c.Assert(count, Equals, 1) 188 | 189 | count, exists = g.DegreeOf("qux") 190 | c.Assert(exists, Equals, true) 191 | c.Assert(count, Equals, 1) 192 | 193 | count, exists = g.DegreeOf("isolate") 194 | c.Assert(exists, Equals, true) 195 | c.Assert(count, Equals, 0) 196 | 197 | count, exists = g.DegreeOf("missing") 198 | c.Assert(exists, Equals, false) 199 | c.Assert(count, Equals, 0) 200 | } 201 | -------------------------------------------------------------------------------- /spec/suite_label.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | ) 9 | 10 | /* LabeledGraphSuite - tests for labeled graphs */ 11 | 12 | type LabeledGraphSuite struct { 13 | Factory func(GraphSource) LabeledGraph 14 | } 15 | 16 | func (s *LabeledGraphSuite) SuiteLabel() string { 17 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 18 | } 19 | 20 | func (s *LabeledGraphSuite) TestEdges(c *C) { 21 | // This method is not redundant with the base Graph suite as it ensures that the edges 22 | // provided by the Edges() iterator actually do implement LabeledEdge. 23 | g := s.Factory(GraphFixtures["l-2e3v"]) 24 | 25 | var we LabeledEdge 26 | g.Edges(func(e Edge) (terminate bool) { 27 | c.Assert(e, Implements, &we) 28 | return 29 | }) 30 | } 31 | 32 | func (s *LabeledGraphSuite) TestHasLabeledEdge(c *C) { 33 | g := s.Factory(GraphFixtures["l-2e3v"]) 34 | 35 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "foo")), Equals, true) 36 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(2, 1, "foo")), Equals, true) // both directions work 37 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "qux")), Equals, false) // wrong label 38 | } 39 | 40 | type LabeledDigraphSuite struct { 41 | Factory func(GraphSource) LabeledGraph 42 | } 43 | 44 | func (s *LabeledDigraphSuite) SuiteLabel() string { 45 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 46 | } 47 | 48 | func (s *LabeledDigraphSuite) TestArcSubtypeImplementation(c *C) { 49 | // This method is not redundant with the base Graph suite as it ensures that the edges 50 | // provided by the Arcs() iterator actually do implement LabeledArc. 51 | g := s.Factory(GraphFixtures["l-2e3v"]).(LabeledDigraph) 52 | 53 | var hit int // just internal safety check to ensure the fixture is good and hits 54 | var wa LabeledArc 55 | g.Arcs(func(e Arc) (terminate bool) { 56 | hit++ 57 | c.Assert(e, Implements, &wa) 58 | return 59 | }) 60 | 61 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 62 | hit++ 63 | c.Assert(e, Implements, &wa) 64 | return 65 | }) 66 | 67 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 68 | hit++ 69 | c.Assert(e, Implements, &wa) 70 | return 71 | }) 72 | 73 | c.Assert(hit, Equals, 4) 74 | } 75 | 76 | /* LabeledEdgeSetMutatorSuite - tests for mutable labeled graphs */ 77 | 78 | type LabeledEdgeSetMutatorSuite struct { 79 | Factory func(GraphSource) LabeledGraph 80 | } 81 | 82 | func (s *LabeledEdgeSetMutatorSuite) SuiteLabel() string { 83 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 84 | } 85 | 86 | func (s *LabeledEdgeSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 87 | g := s.Factory(NullGraph) 88 | m := g.(LabeledEdgeSetMutator) 89 | 90 | m.AddEdges() 91 | c.Assert(Order(g), Equals, 0) 92 | c.Assert(Size(g), Equals, 0) 93 | 94 | m.RemoveEdges() 95 | c.Assert(Order(g), Equals, 0) 96 | c.Assert(Size(g), Equals, 0) 97 | } 98 | 99 | func (s *LabeledEdgeSetMutatorSuite) TestAddRemoveEdge(c *C) { 100 | g := s.Factory(NullGraph) 101 | m := g.(LabeledEdgeSetMutator) 102 | 103 | m.AddEdges(NewLabeledEdge(1, 2, "foo")) 104 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "foo")), Equals, true) 105 | 106 | // Now test removal 107 | m.RemoveEdges(NewLabeledEdge(1, 2, "foo")) 108 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 109 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "foo")), Equals, false) 110 | } 111 | 112 | func (s *LabeledEdgeSetMutatorSuite) TestMultiAddRemoveEdge(c *C) { 113 | g := s.Factory(NullGraph) 114 | m := g.(LabeledEdgeSetMutator) 115 | 116 | m.AddEdges(NewLabeledEdge(1, 2, "foo"), NewLabeledEdge(2, 3, "bar")) 117 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "foo")), Equals, true) 118 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(2, 3, "bar")), Equals, true) 119 | 120 | // Now test removal 121 | m.RemoveEdges(NewLabeledEdge(1, 2, "foo"), NewLabeledEdge(2, 3, "bar")) 122 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(1, 2, "foo")), Equals, false) 123 | c.Assert(g.HasLabeledEdge(NewLabeledEdge(2, 3, "bar")), Equals, false) 124 | } 125 | 126 | /* LabeledArcSetMutatorSuite - tests for mutable labeled graphs */ 127 | 128 | type LabeledArcSetMutatorSuite struct { 129 | Factory func(GraphSource) LabeledGraph 130 | } 131 | 132 | func (s *LabeledArcSetMutatorSuite) SuiteLabel() string { 133 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 134 | } 135 | 136 | func (s *LabeledArcSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 137 | g := s.Factory(NullGraph).(LabeledDigraph) 138 | m := g.(LabeledArcSetMutator) 139 | 140 | m.AddArcs() 141 | c.Assert(Order(g), Equals, 0) 142 | c.Assert(Size(g), Equals, 0) 143 | 144 | m.RemoveArcs() 145 | c.Assert(Order(g), Equals, 0) 146 | c.Assert(Size(g), Equals, 0) 147 | } 148 | 149 | func (s *LabeledArcSetMutatorSuite) TestAddRemoveHasArc(c *C) { 150 | g := s.Factory(NullGraph).(LabeledDigraph) 151 | m := g.(LabeledArcSetMutator) 152 | 153 | m.AddArcs(NewLabeledArc(1, 2, "foo")) 154 | c.Assert(g.HasLabeledArc(NewLabeledArc(1, 2, "foo")), Equals, true) 155 | c.Assert(g.HasLabeledArc(NewLabeledArc(1, 2, "qux")), Equals, false) // wrong label 156 | 157 | // Now test removal 158 | m.RemoveArcs(NewLabeledArc(1, 2, "foo")) 159 | c.Assert(g.HasLabeledArc(NewLabeledArc(1, 2, "foo")), Equals, false) 160 | } 161 | 162 | func (s *LabeledArcSetMutatorSuite) TestMultiAddRemoveHasArc(c *C) { 163 | g := s.Factory(NullGraph).(LabeledDigraph) 164 | m := g.(LabeledArcSetMutator) 165 | 166 | m.AddArcs(NewLabeledArc(1, 2, "foo"), NewLabeledArc(2, 3, "bar")) 167 | c.Assert(g.HasLabeledArc(NewLabeledArc(1, 2, "foo")), Equals, true) 168 | c.Assert(g.HasLabeledArc(NewLabeledArc(2, 3, "bar")), Equals, true) 169 | 170 | m.RemoveArcs(NewLabeledArc(1, 2, "foo"), NewLabeledArc(2, 3, "bar")) 171 | c.Assert(g.HasLabeledArc(NewLabeledArc(1, 2, "foo")), Equals, false) 172 | c.Assert(g.HasLabeledArc(NewLabeledArc(2, 3, "bar")), Equals, false) 173 | } 174 | -------------------------------------------------------------------------------- /spec/suite_simple.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | 7 | . "github.com/sdboyer/gocheck" 8 | . "github.com/sdboyer/gogl" 9 | ) 10 | 11 | /* SimpleGraphSuite - tests for simple graph methods */ 12 | 13 | type SimpleGraphSuite struct { 14 | Factory func(GraphSource) Graph 15 | Directed bool 16 | } 17 | 18 | func (s *SimpleGraphSuite) SuiteLabel() string { 19 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 20 | } 21 | 22 | func (s *SimpleGraphSuite) TestDensity(c *C) { 23 | c.Assert(math.IsNaN(s.Factory(NullGraph).(SimpleGraph).Density()), Equals, true) 24 | 25 | g := s.Factory(GraphFixtures["pair"]).(SimpleGraph) 26 | if s.Directed { 27 | c.Assert(g.Density(), Equals, float64(0.5)) 28 | } else { 29 | c.Assert(g.Density(), Equals, float64(1)) 30 | } 31 | 32 | g = s.Factory(GraphFixtures["2e3v"]).(SimpleGraph) 33 | if s.Directed { 34 | c.Assert(g.Density(), Equals, float64(2)/float64(6)) 35 | } else { 36 | c.Assert(g.Density(), Equals, float64(2)/float64(3)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /spec/suite_weighted.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | ) 9 | 10 | /* WeightedGraphSuite - tests for weighted graphs */ 11 | 12 | type WeightedGraphSuite struct { 13 | Factory func(GraphSource) WeightedGraph 14 | } 15 | 16 | func (s *WeightedGraphSuite) SuiteLabel() string { 17 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 18 | } 19 | 20 | func (s *WeightedGraphSuite) TestEdges(c *C) { 21 | // This method is not redundant with the base Graph suite as it ensures that the edges 22 | // provided by the Edges() iterator actually do implement WeightedEdge. 23 | g := s.Factory(GraphFixtures["w-2e3v"]) 24 | 25 | var we WeightedEdge 26 | g.Edges(func(e Edge) (terminate bool) { 27 | c.Assert(e, Implements, &we) 28 | return 29 | }) 30 | } 31 | 32 | func (s *WeightedGraphSuite) TestHasWeightedEdge(c *C) { 33 | g := s.Factory(GraphFixtures["w-2e3v"]) 34 | 35 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, 5.23)), Equals, true) 36 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(2, 1, 5.23)), Equals, true) // both directions work 37 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, -3.7212)), Equals, false) // wrong weight 38 | } 39 | 40 | type WeightedDigraphSuite struct { 41 | Factory func(GraphSource) WeightedGraph 42 | } 43 | 44 | func (s *WeightedDigraphSuite) SuiteLabel() string { 45 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 46 | } 47 | 48 | func (s *WeightedDigraphSuite) TestArcSubtypeImplementation(c *C) { 49 | // This method is not redundant with the base Graph suite as it ensures that the edges 50 | // provided by the Arcs() iterator actually do implement WeightedArc. 51 | g := s.Factory(GraphFixtures["w-2e3v"]).(WeightedDigraph) 52 | 53 | var hit int // just internal safety check to ensure the fixture is good and hits 54 | var wa WeightedArc 55 | g.Arcs(func(e Arc) (terminate bool) { 56 | hit++ 57 | c.Assert(e, Implements, &wa) 58 | return 59 | }) 60 | 61 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 62 | hit++ 63 | c.Assert(e, Implements, &wa) 64 | return 65 | }) 66 | 67 | g.ArcsFrom(2, func(e Arc) (terminate bool) { 68 | hit++ 69 | c.Assert(e, Implements, &wa) 70 | return 71 | }) 72 | 73 | c.Assert(hit, Equals, 4) 74 | } 75 | 76 | /* WeightedEdgeSetMutatorSuite - tests for mutable weighted graphs */ 77 | 78 | type WeightedEdgeSetMutatorSuite struct { 79 | Factory func(GraphSource) WeightedGraph 80 | } 81 | 82 | func (s *WeightedEdgeSetMutatorSuite) SuiteLabel() string { 83 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 84 | } 85 | 86 | func (s *WeightedEdgeSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 87 | g := s.Factory(NullGraph) 88 | m := g.(WeightedEdgeSetMutator) 89 | 90 | m.AddEdges() 91 | c.Assert(Order(g), Equals, 0) 92 | c.Assert(Size(g), Equals, 0) 93 | 94 | m.RemoveEdges() 95 | c.Assert(Order(g), Equals, 0) 96 | c.Assert(Size(g), Equals, 0) 97 | } 98 | 99 | func (s *WeightedEdgeSetMutatorSuite) TestAddRemoveEdge(c *C) { 100 | g := s.Factory(NullGraph) 101 | m := g.(WeightedEdgeSetMutator) 102 | 103 | m.AddEdges(NewWeightedEdge(1, 2, 5.23)) 104 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, 5.23)), Equals, true) 105 | 106 | // Now test removal 107 | m.RemoveEdges(NewWeightedEdge(1, 2, 5.23)) 108 | c.Assert(g.HasEdge(NewEdge(1, 2)), Equals, false) 109 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, 5.23)), Equals, false) 110 | } 111 | 112 | func (s *WeightedEdgeSetMutatorSuite) TestMultiAddRemoveEdge(c *C) { 113 | g := s.Factory(NullGraph) 114 | m := g.(WeightedEdgeSetMutator) 115 | 116 | m.AddEdges(NewWeightedEdge(1, 2, 5.23), NewWeightedEdge(2, 3, 5.821)) 117 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, 5.23)), Equals, true) 118 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(2, 3, 5.821)), Equals, true) 119 | 120 | // Now test removal 121 | m.RemoveEdges(NewWeightedEdge(1, 2, 5.23), NewWeightedEdge(2, 3, 5.821)) 122 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(1, 2, 5.23)), Equals, false) 123 | c.Assert(g.HasWeightedEdge(NewWeightedEdge(2, 3, 5.821)), Equals, false) 124 | } 125 | 126 | /* WeightedArcSetMutatorSuite - tests for mutable weighted graphs */ 127 | 128 | type WeightedArcSetMutatorSuite struct { 129 | Factory func(GraphSource) WeightedGraph 130 | } 131 | 132 | func (s *WeightedArcSetMutatorSuite) SuiteLabel() string { 133 | return fmt.Sprintf("%T", s.Factory(NullGraph)) 134 | } 135 | 136 | func (s *WeightedArcSetMutatorSuite) TestGracefulEmptyVariadics(c *C) { 137 | g := s.Factory(NullGraph).(WeightedDigraph) 138 | m := g.(WeightedArcSetMutator) 139 | 140 | m.AddArcs() 141 | c.Assert(Order(g), Equals, 0) 142 | c.Assert(Size(g), Equals, 0) 143 | 144 | m.RemoveArcs() 145 | c.Assert(Order(g), Equals, 0) 146 | c.Assert(Size(g), Equals, 0) 147 | } 148 | 149 | func (s *WeightedArcSetMutatorSuite) TestAddRemoveHasArc(c *C) { 150 | g := s.Factory(NullGraph).(WeightedDigraph) 151 | m := g.(WeightedArcSetMutator) 152 | 153 | m.AddArcs(NewWeightedArc(1, 2, 5.23)) 154 | c.Assert(g.HasWeightedArc(NewWeightedArc(1, 2, 5.23)), Equals, true) 155 | c.Assert(g.HasWeightedArc(NewWeightedArc(1, 2, -3.7212)), Equals, false) // wrong weight 156 | 157 | // Now test removal 158 | m.RemoveArcs(NewWeightedArc(1, 2, 5.23)) 159 | c.Assert(g.HasWeightedArc(NewWeightedArc(1, 2, 5.23)), Equals, false) 160 | } 161 | 162 | func (s *WeightedArcSetMutatorSuite) TestMultiAddRemoveHasArc(c *C) { 163 | g := s.Factory(NullGraph).(WeightedDigraph) 164 | m := g.(WeightedArcSetMutator) 165 | 166 | m.AddArcs(NewWeightedArc(1, 2, 5.23), NewWeightedArc(2, 3, 5.821)) 167 | c.Assert(g.HasWeightedArc(NewWeightedArc(1, 2, 5.23)), Equals, true) 168 | c.Assert(g.HasWeightedArc(NewWeightedArc(2, 3, 5.821)), Equals, true) 169 | 170 | m.RemoveArcs(NewWeightedArc(1, 2, 5.23), NewWeightedArc(2, 3, 5.821)) 171 | c.Assert(g.HasWeightedArc(NewWeightedArc(1, 2, 5.23)), Equals, false) 172 | c.Assert(g.HasWeightedArc(NewWeightedArc(2, 3, 5.821)), Equals, false) 173 | } 174 | -------------------------------------------------------------------------------- /test-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # run on CI service w/something like: 4 | # 5 | # go get github.com/axw/gocov/gocov 6 | # go get github.com/mattn/goveralls 7 | # COVERALLS="-service drone.io -repotoken $COVERALLS_TOKEN" ./test-coverage.sh 8 | # 9 | 10 | echo "mode: set" > acc.out 11 | fail=0 12 | 13 | # Standard go tooling behavior is to ignore dirs with leading underscors 14 | for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); 15 | do 16 | if ls $dir/*.go &> /dev/null; then 17 | go test -coverprofile=profile.out $dir || fail=1 18 | if [ -f profile.out ] 19 | then 20 | cat profile.out | grep -v "mode: set" >> acc.out 21 | rm profile.out 22 | fi 23 | fi 24 | done 25 | 26 | # Failures have incomplete results, so don't send 27 | if [ -n "$COVERALLS" ] && [ "$fail" -eq 0 ] 28 | then 29 | goveralls -v -coverprofile=acc.out $COVERALLS 30 | fi 31 | 32 | rm -f acc.out 33 | 34 | exit $fail 35 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | // Returns the number of vertices in a graph. 4 | // 5 | // If available, this function will take advantage of the optional optimization Order() method. 6 | // Otherwise, it will iterate through all vertices in the graph. Thus, if your use case involves 7 | // iterating through all the graph's vertices, it is better to simply check for the VertexCounter 8 | // interface yourself. 9 | func Order(g VertexEnumerator) int { 10 | if c, ok := g.(VertexCounter); ok { 11 | return c.Order() 12 | } else { 13 | var order int 14 | g.Vertices(func(v Vertex) (terminate bool) { 15 | order++ 16 | return 17 | }) 18 | 19 | return order 20 | } 21 | } 22 | 23 | // Returns the number of edges in a graph. 24 | // 25 | // If available, this function will take advantage of the optional optimization Size() method. 26 | // Otherwise, it will iterate through all edges in the graph. Thus, if your use case involves 27 | // iterating through all the graph's edges, it is better to simply check for the EdgeCounter 28 | // interface yourself. 29 | func Size(g EdgeEnumerator) int { 30 | if c, ok := g.(EdgeCounter); ok { 31 | return c.Size() 32 | } else { 33 | var size int 34 | g.Edges(func(e Edge) (terminate bool) { 35 | size++ 36 | return 37 | }) 38 | return size 39 | } 40 | } 41 | 42 | /* Enumerator to slice/collection functors */ 43 | 44 | // Collects all of a graph's vertices into a vertex slice, for easy range-ing. 45 | // 46 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 47 | func CollectVertices(g VertexEnumerator) (vertices []Vertex) { 48 | if c, ok := g.(VertexCounter); ok { 49 | // If possible, size the slice based on the number of vertices the graph reports it has 50 | vertices = make([]Vertex, 0, c.Order()) 51 | } else { 52 | // Otherwise just pick something...reasonable? 53 | vertices = make([]Vertex, 0, 32) 54 | } 55 | 56 | g.Vertices(func(v Vertex) (terminate bool) { 57 | vertices = append(vertices, v) 58 | return 59 | }) 60 | 61 | return vertices 62 | } 63 | 64 | // Collects all of a given vertex's adjacent vertices into a vertex slice, for easy range-ing. 65 | // 66 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 67 | func CollectVerticesAdjacentTo(v Vertex, g AdjacencyEnumerator) (vertices []Vertex) { 68 | if c, ok := g.(DegreeChecker); ok { 69 | // If possible, size the slice based on the number of adjacent vertices the graph reports 70 | deg, _ := c.DegreeOf(v) 71 | vertices = make([]Vertex, 0, deg) 72 | } else { 73 | // Otherwise just pick something...reasonable? 74 | vertices = make([]Vertex, 0, 8) 75 | } 76 | 77 | g.AdjacentTo(v, func(v Vertex) (terminate bool) { 78 | vertices = append(vertices, v) 79 | return 80 | }) 81 | 82 | return vertices 83 | } 84 | 85 | // Collects all of a graph's edges into an edge slice, for easy range-ing. 86 | // 87 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 88 | func CollectEdges(g EdgeEnumerator) (edges []Edge) { 89 | if c, ok := g.(EdgeCounter); ok { 90 | // If possible, size the slice based on the number of edges the graph reports it has 91 | edges = make([]Edge, 0, c.Size()) 92 | } else { 93 | // Otherwise just pick something...reasonable? 94 | edges = make([]Edge, 0, 32) 95 | } 96 | 97 | g.Edges(func(e Edge) (terminate bool) { 98 | edges = append(edges, e) 99 | return 100 | }) 101 | 102 | return edges 103 | } 104 | 105 | // Collects all of a given vertex's incident edges into an edge slice, for easy range-ing. 106 | // 107 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 108 | func CollectEdgesIncidentTo(v Vertex, g IncidentEdgeEnumerator) (edges []Edge) { 109 | if c, ok := g.(DegreeChecker); ok { 110 | // If possible, size the slice based on the number of incident edges the graph reports 111 | deg, _ := c.DegreeOf(v) 112 | edges = make([]Edge, 0, deg) 113 | } else { 114 | // Otherwise just pick something...reasonable? 115 | edges = make([]Edge, 0, 8) 116 | } 117 | 118 | g.IncidentTo(v, func(e Edge) (terminate bool) { 119 | edges = append(edges, e) 120 | return 121 | }) 122 | 123 | return edges 124 | } 125 | 126 | // Collects all of a given vertex's out-arcs into an arc slice, for easy range-ing. 127 | // 128 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 129 | func CollectArcsFrom(v Vertex, g IncidentArcEnumerator) (arcs []Arc) { 130 | if c, ok := g.(DirectedDegreeChecker); ok { 131 | // If possible, size the slice based on the number of out-arcs the graph reports 132 | deg, _ := c.OutDegreeOf(v) 133 | arcs = make([]Arc, 0, deg) 134 | } else { 135 | // Otherwise just pick something...reasonable? 136 | arcs = make([]Arc, 0, 8) 137 | } 138 | 139 | g.ArcsFrom(v, func(e Arc) (terminate bool) { 140 | arcs = append(arcs, e) 141 | return 142 | }) 143 | 144 | return arcs 145 | } 146 | 147 | // Collects all of a given vertex's in-arcs into an arc slice, for easy range-ing. 148 | // 149 | // This is a convenience function. Avoid it on very large graphs or in performance critical sections. 150 | func CollectArcsTo(v Vertex, g IncidentArcEnumerator) (arcs []Arc) { 151 | if c, ok := g.(DirectedDegreeChecker); ok { 152 | // If possible, size the slice based on the number of in-arcs the graph reports 153 | deg, _ := c.InDegreeOf(v) 154 | arcs = make([]Arc, 0, deg) 155 | } else { 156 | // Otherwise just pick something...reasonable? 157 | arcs = make([]Arc, 0, 8) 158 | } 159 | 160 | g.ArcsTo(v, func(e Arc) (terminate bool) { 161 | arcs = append(arcs, e) 162 | return 163 | }) 164 | 165 | return arcs 166 | } 167 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package gogl_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/sdboyer/gocheck" 7 | . "github.com/sdboyer/gogl" 8 | "github.com/sdboyer/gogl/spec" 9 | "gopkg.in/fatih/set.v0" 10 | ) 11 | 12 | // Hook gocheck into the go test runner 13 | func TestHookup(t *testing.T) { TestingT(t) } 14 | 15 | // Tests for collection functors 16 | type CollectionFunctorsSuite struct{} 17 | 18 | var _ = Suite(&CollectionFunctorsSuite{}) 19 | 20 | func (s *CollectionFunctorsSuite) TestCollectVertices(c *C) { 21 | slice := CollectVertices(spec.GraphLiteralFixture(true)) 22 | 23 | c.Assert(len(slice), Equals, 4) 24 | 25 | set1 := set.New(set.NonThreadSafe) 26 | for _, v := range slice { 27 | set1.Add(v) 28 | } 29 | 30 | c.Assert(set1.Has("foo"), Equals, true) 31 | c.Assert(set1.Has("bar"), Equals, true) 32 | c.Assert(set1.Has("baz"), Equals, true) 33 | c.Assert(set1.Has("isolate"), Equals, true) 34 | 35 | slice2 := CollectVertices(spec.GraphFixtures["2e3v"]) 36 | 37 | c.Assert(len(slice2), Equals, 3) 38 | 39 | set2 := set.New(set.NonThreadSafe) 40 | for _, v := range slice2 { 41 | set2.Add(v) 42 | } 43 | 44 | c.Assert(set2.Has("foo"), Equals, true) 45 | c.Assert(set2.Has("bar"), Equals, true) 46 | c.Assert(set2.Has("baz"), Equals, true) 47 | } 48 | 49 | func (s *CollectionFunctorsSuite) TestCollectAdjacentVertices(c *C) { 50 | slice := CollectVerticesAdjacentTo("bar", spec.GraphLiteralFixture(true)) 51 | 52 | c.Assert(len(slice), Equals, 2) 53 | 54 | set := set.New(set.NonThreadSafe) 55 | for _, v := range slice { 56 | set.Add(v) 57 | } 58 | 59 | c.Assert(set.Has("foo"), Equals, true) 60 | c.Assert(set.Has("baz"), Equals, true) 61 | } 62 | 63 | func (s *CollectionFunctorsSuite) TestCollectEdges(c *C) { 64 | slice := CollectEdges(spec.GraphLiteralFixture(true)) 65 | 66 | c.Assert(len(slice), Equals, 2) 67 | 68 | set1 := set.New(set.NonThreadSafe) 69 | for _, e := range slice { 70 | set1.Add(e) 71 | } 72 | 73 | c.Assert(set1.Has(NewEdge("foo", "bar")), Equals, true) 74 | c.Assert(set1.Has(NewEdge("bar", "baz")), Equals, true) 75 | 76 | slice2 := CollectEdges(spec.GraphFixtures["2e3v"]) 77 | 78 | c.Assert(len(slice2), Equals, 2) 79 | 80 | set2 := set.New(set.NonThreadSafe) 81 | for _, e := range slice2 { 82 | set2.Add(NewEdge(e.Both())) 83 | } 84 | 85 | c.Assert(set2.Has(NewEdge("foo", "bar")), Equals, true) 86 | c.Assert(set2.Has(NewEdge("bar", "baz")), Equals, true) 87 | } 88 | 89 | func (s *CollectionFunctorsSuite) TestCollectEdgesIncidentTo(c *C) { 90 | slice := CollectEdgesIncidentTo("foo", spec.GraphLiteralFixture(true)) 91 | 92 | c.Assert(len(slice), Equals, 1) 93 | 94 | set := set.New(set.NonThreadSafe) 95 | for _, e := range slice { 96 | set.Add(e) 97 | } 98 | 99 | c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true) 100 | } 101 | 102 | func (s *CollectionFunctorsSuite) TestCollectArcsFrom(c *C) { 103 | slice := CollectArcsFrom("foo", spec.GraphLiteralFixture(true)) 104 | 105 | c.Assert(len(slice), Equals, 1) 106 | 107 | set := set.New(set.NonThreadSafe) 108 | for _, e := range slice { 109 | set.Add(e) 110 | } 111 | 112 | c.Assert(set.Has(NewArc("foo", "bar")), Equals, true) 113 | } 114 | 115 | func (s *CollectionFunctorsSuite) TestCollectArcsTo(c *C) { 116 | slice := CollectArcsTo("bar", spec.GraphLiteralFixture(true)) 117 | 118 | c.Assert(len(slice), Equals, 1) 119 | 120 | set := set.New(set.NonThreadSafe) 121 | for _, e := range slice { 122 | set.Add(e) 123 | } 124 | 125 | c.Assert(set.Has(NewArc("foo", "bar")), Equals, true) 126 | } 127 | 128 | type CountingFunctorsSuite struct{} 129 | 130 | var _ = Suite(&CountingFunctorsSuite{}) 131 | 132 | func (s *CountingFunctorsSuite) TestOrder(c *C) { 133 | el := EdgeList{ 134 | NewEdge("foo", "bar"), 135 | NewEdge("bar", "baz"), 136 | NewEdge("foo", "qux"), 137 | NewEdge("qux", "bar"), 138 | } 139 | c.Assert(Order(el), Equals, 4) 140 | c.Assert(Order(spec.GraphLiteralFixture(true)), Equals, 4) 141 | } 142 | 143 | func (s *CountingFunctorsSuite) TestSize(c *C) { 144 | el := EdgeList{ 145 | NewEdge("foo", "bar"), 146 | NewEdge("bar", "baz"), 147 | NewEdge("foo", "qux"), 148 | NewEdge("qux", "bar"), 149 | } 150 | c.Assert(Size(el), Equals, 4) 151 | c.Assert(Size(spec.GraphLiteralFixture(true)), Equals, 2) 152 | } 153 | -------------------------------------------------------------------------------- /vertex.go: -------------------------------------------------------------------------------- 1 | package gogl 2 | 3 | /* Vertex structures */ 4 | 5 | // A Vertex in gogl is a value of empty interface type. 6 | // 7 | // This is largely comensurate with graph theory, as vertices are, in the general 8 | // case, known to be unique within the graph context, but the particular property 9 | // that makes them unique has no bearing on the graph's behavior. In Go-speak, that 10 | // translates pretty nicely to interface{}. 11 | type Vertex interface{} 12 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.2 2 | 3 | --------------------------------------------------------------------------------