Skip to content

Commit 376b9a9

Browse files
committed
updated CHANGELOG, removed class conformance from Graph, updated some documentation, renamed CONTRIBUTOR
1 parent b2c02ab commit 376b9a9

12 files changed

+87
-26
lines changed

.swift-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.2
1+
5.0

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
### 3.0.0
2+
- **This is an API breaking refactor**
3+
- This version requires Swift 5.0 (Xcode 10.2)
4+
- `Graph` no longer conforms to `class` so structs can be `Graph`s (@davecom)
5+
- `Graph` and `Edge` are now `Codable` meaning all implementations must be as well including all vertex types, leading to the removal of `CodableUnweightedGraph` and `CodableWeightedGraph`, which are now unnecessary (@davecom)
6+
- New CONTRIBUTORS.md file containing some history (@davecom)
7+
- New search traversal methods (@ferranpujolcamins)
8+
- Improvements to `UniqueElementsGraph` (@ferranpujolcamins)
9+
- Useful constructors for testing (@ferranpujolcamins)
10+
- Improvements to `UniqueElementsGraph` tests (@ferranpujolcamins)
11+
- Refactor many aspects of `UnweightedGraph` and `WeightedGraph` into conditional conformance extensions to `Graph` and new protocols (@ferranpujolcamins)
12+
- Add new performance tests (@ferranpujolcamins)
13+
- Add direction back to `Edge` (@ferranpujolcamins)
14+
115
### 2.0.0
216
- **This is an API breaking refactor**
3-
- This version requires Swift 4.2 (Xcode 10)
17+
- This version requires Swift 4.2 (Xcode 10.1)
418
- `Graph` is now a protocol instead of a class
519
- `edgesToVertices()` is now a method on `Graph` instead of a free function
620
- The `Edge` protocol has been significantly simplifieid
File renamed without changes.

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ There is a large amount of documentation in the source code using the latest App
118118
### Edges
119119
Edges connect the vertices in your graph to one another.
120120

121-
* `Edge` (Protocol) - A protocol that all edges in a graph must conform to. An edge is a connection between two vertices in the graph. The vertices are specified by their index in the graph which is an integer.
121+
* `Edge` (Protocol) - A protocol that all edges in a graph must conform to. An edge is a connection between two vertices in the graph. The vertices are specified by their index in the graph which is an integer. All `Edge`s must be `Codable`.
122122
* `UnweightedEdge` - This is a concrete implementation of `Edge` for unweighted graphs.
123-
* `WeightedEdge` - A subclass of `UnweightedEdge` that adds weights. Weights are a generic type - they can be anything that implements `Comparable`, `Numeric` and `Codable`. Typical weight types are `Int` and `Float`.
123+
* `WeightedEdge` - This is a concrete implementation of `Edge` for weighted graphs. Weights are a generic type - they can be anything that implements `Comparable`, `Numeric` and `Codable`. Typical weight types are `Int` and `Float`.
124124

125125
### Graphs
126126
Graphs are the data structures at the heart of SwiftGraph. All vertices are assigned an integer index when they are inserted into a graph and it's generally faster to refer to them by their index than by the vertex's actual object.
127127

128-
Graphs implement the standard Swift protocols `Collection` (for iterating through all vertices and for grabbing a vertex by its index through a subscript). For instance, the following example prints all vertices in a Graph on separate lines:
128+
Graphs implement the standard Swift protocols `Collection` (for iterating through all vertices and for grabbing a vertex by its index through a subscript) and `Codable` . For instance, the following example prints all vertices in a Graph on separate lines:
129129
```swift
130130
for v in g { // g is a Graph<String>
131131
print(v)
@@ -138,7 +138,7 @@ print(g[23]) // g is a Graph<String>
138138

139139
Note: At this time, graphs are *not* thread-safe. However, once a graph is constructed, if you will only be doing lookups and searches through it (no removals of vertices/edges and no additions of vertices/edges) then you should be able to do that from multiple threads. A fully thread-safe graph implementation is a possible future direction.
140140

141-
* `Graph` (Protocol) - This is the base protocol for all graphs. Generally, you should use one of its canonical class implementations, `UnweightedGraph` or `WeightedGraph`, instead of rolling your own adopter, because they offer significant built-in functionality. The vertices in a `Graph` (defined as a generic at graph creation time) can be of any type that conforms to `Equatable`. `Graph` has methods for:
141+
* `Graph` (Protocol) - This is the base protocol for all graphs. Generally, you should use one of its canonical class implementations, `UnweightedGraph` or `WeightedGraph`, instead of rolling your own adopter, because they offer significant built-in functionality. The vertices in a `Graph` (defined as a generic at graph creation time) can be of any type that conforms to `Equatable` and `Codable`. All `Graph`s are `Codable`. `Graph` has methods for:
142142
* Adding a vertex
143143
* Getting the index of a vertex
144144
* Finding the neighbors of an index/vertex
@@ -150,7 +150,6 @@ Note: At this time, graphs are *not* thread-safe. However, once a graph is const
150150
* Removing a particular vertex (all other edge relationships are automatically updated at the same time (because the indices of their connections changes) so this is slow - O(v + e) where v is the number of vertices and e is the number of edges)
151151
* `UnweightedGraph` - A generic class implementation of `Graph` that adds convenience methods for adding and removing edges of type `UnweightedEdge`. `UnweightedGraph` is generic over the type of the vertices.
152152
* `WeightedGraph` - A generic class implementation of `Graph` that adds convenience methods for adding and removing edges of type `WeightedEdge`. `WeightedGraph` also adds a method for returning a list of tuples containing all of the neighbor vertices of an index along with their respective weights. `WeightedGraph` is generic over the types of the vertices and its weights.
153-
* `CodableUnweightedGraph` and `CodableWeightedGraph` - Subclasses of `UnweightedGraph` and `WeightedGraph` that add support for the `Codable` protocol. Their vertex and weight types must also be `Codable`. This allows serialization of graphs to formats like JSON.
154153
* `UniqueElementsGraph` - an experimental subclass of `UnweightedGraph` with support for union operations that ensures all vertices and edges in a graph are unique.
155154

156155
### Search
@@ -159,6 +158,7 @@ Search methods are defined in extensions of `Graph` and `WeightedGraph` in `Sear
159158
* `dfs()` (method on `Graph`) - Finds a path from one vertex to another in a `Graph` using a depth-first search. Returns an array of `Edge`s going from the source vertex to the destination vertex or an empty array if no path could be found. A version of this method takes a function, `goalTest()`, that operates on a vertex and returns a boolean to indicate whether it is a goal for the search. It returns a path to the first vertex that returns true from `goalTest()`.
160159
* `findAll()` Uses a breadth-first search to find all connected vertices from the starting vertex that return true when run through a `goalTest()` function. Paths to the connected vertices are returned in an array, which is empty if no vertices are found.
161160
* `dijkstra()` (method on `WeightedGraph`) - Finds the shortest path from a starting vertex to every other vertex in a `WeightedGraph`. Returns a tuple who's first element is an array of the distances to each vertex in the graph arranged by index. The second element of the tuple is a dictionary mapping graph indices to the previous `Edge` that gets them there in the shortest time from the staring vertex. Using this dictionary and the function `pathDictToPath()`, you can find the shortest path from the starting vertex to any other connected vertex. See the `dijkstra()` unit tests in `DijkstraGraphTests.swift` for a demo of this.
161+
* Graph traversal versions of `bfs()` and `dfs()` that allow a visit function to execute at each stop
162162

163163
### Sort & Miscellaneous
164164
An extension to `Graph` in `Sort.swift` provides facilities for topological sort and detecting a DAG.
@@ -172,7 +172,7 @@ An extension to `Graph` in `Cycles.swift` finds all of the cycles in a graph.
172172
* `detectCycles()` - Uses an algorithm developed by Liu/Wang to find all of the cycles in a graph. Optionally, this method can take one parameter, `upToLength`, that specifies a length at which to stop searching for cycles. For instance, if `upToLength` is 3, `detectCycles()` will find all of the 1 vertex cycles (self-cycles, vertices with edges to themselves), and 3 vertex cycles (connection to another vertex and back again, present in all undirected graphs with more than 1 vertex). There is no such thing as a 2 vertex cycle.
173173

174174
## Authorship, License, & Contributors
175-
SwiftGraph is written by David Kopec and released under the Apache License (see `LICENSE`). You can find my email address on my GitHub profile page. I encourage you to submit pull requests and open issues here on GitHub.
175+
SwiftGraph is written by David Kopec and other contributors (see `CONTRIBUTORS.md`). It is released under the Apache License (see `LICENSE`). You can find my email address on my GitHub profile page. I encourage you to submit pull requests and open issues here on GitHub.
176176

177177
I would like to thank all of the contributors who have helped improve SwiftGraph over the years, and have kept me motivated. Contributing to SwiftGraph, in-line with the Apache license, means also releasing your contribution under the same license as the original project. However, the Apache license is permissive, and you are free to include SwiftGraph in a commercial, closed source product as long as you give it & its author credit (in fact SwiftGraph has already found its way into several products). See `LICENSE` for details.
178178

@@ -181,4 +181,4 @@ Future directions for this project to take could include:
181181
* More utility functions
182182
* A thread safe implementation of `Graph`
183183
* More extensive performance testing
184-
* GraphML Support
184+
* GraphML and Other Format Support

Sources/SwiftGraph/Graph.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/// The protocol for all graphs.
2020
/// You should generally use one of its two canonical class implementations,
2121
/// *UnweightedGraph* and *WeightedGraph*
22-
public protocol Graph: class, CustomStringConvertible, Collection, Codable {
22+
public protocol Graph: CustomStringConvertible, Collection, Codable {
2323
associatedtype V: Equatable & Codable
2424
associatedtype E: Edge & Equatable
2525
var vertices: [V] { get set }
@@ -128,7 +128,7 @@ extension Graph {
128128
///
129129
/// - parameter v: The vertex to be added.
130130
/// - returns: The index where the vertex was added.
131-
public func addVertex(_ v: V) -> Int {
131+
public mutating func addVertex(_ v: V) -> Int {
132132
vertices.append(v)
133133
edges.append([E]())
134134
return vertices.count - 1
@@ -140,7 +140,7 @@ extension Graph {
140140
/// - parameter directed: If false, undirected edges are created.
141141
/// If true, a reversed edge is also created.
142142
/// Default is false.
143-
public func addEdge(_ e: E, directed: Bool = false) {
143+
public mutating func addEdge(_ e: E, directed: Bool = false) {
144144
edges[e.u].append(e)
145145
if !directed && e.u != e.v {
146146
edges[e.v].append(e.reversed())
@@ -152,7 +152,7 @@ extension Graph {
152152
/// - parameter from: The starting vertex's index.
153153
/// - parameter to: The ending vertex's index.
154154
/// - parameter bidirectional: Remove edges coming back (to -> from)
155-
public func removeAllEdges(from: Int, to: Int, bidirectional: Bool = true) {
155+
public mutating func removeAllEdges(from: Int, to: Int, bidirectional: Bool = true) {
156156
edges[from].removeAll(where: { $0.v == to })
157157

158158
if bidirectional {
@@ -165,7 +165,7 @@ extension Graph {
165165
/// - parameter from: The starting vertex.
166166
/// - parameter to: The ending vertex.
167167
/// - parameter bidirectional: Remove edges coming back (to -> from)
168-
public func removeAllEdges(from: V, to: V, bidirectional: Bool = true) {
168+
public mutating func removeAllEdges(from: V, to: V, bidirectional: Bool = true) {
169169
if let u = indexOfVertex(from) {
170170
if let v = indexOfVertex(to) {
171171
removeAllEdges(from: u, to: v, bidirectional: bidirectional)
@@ -176,7 +176,7 @@ extension Graph {
176176
/// Remove the first edge found to be equal to *e*
177177
///
178178
/// - parameter e: The edge to remove.
179-
public func removeEdge(_ e: E) {
179+
public mutating func removeEdge(_ e: E) {
180180
if let index = edges[e.u].firstIndex(where: { $0 == e }) {
181181
edges[e.u].remove(at: index)
182182
}
@@ -185,7 +185,7 @@ extension Graph {
185185
/// Removes a vertex at a specified index, all of the edges attached to it, and renumbers the indexes of the rest of the edges.
186186
///
187187
/// - parameter index: The index of the vertex.
188-
public func removeVertexAtIndex(_ index: Int) {
188+
public mutating func removeVertexAtIndex(_ index: Int) {
189189
//remove all edges ending at the vertex, first doing the ones below it
190190
//renumber edges that end after the index
191191
for j in 0..<index {
@@ -231,7 +231,7 @@ extension Graph {
231231
/// Removes the first occurence of a vertex, all of the edges attached to it, and renumbers the indexes of the rest of the edges.
232232
///
233233
/// - parameter vertex: The vertex to be removed..
234-
public func removeVertex(_ vertex: V) {
234+
public mutating func removeVertex(_ vertex: V) {
235235
if let i = indexOfVertex(vertex) {
236236
removeVertexAtIndex(i)
237237
}

Sources/SwiftGraph/UniqueElementsGraph.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
public typealias UnweightedUniqueElementsGraph<V: Equatable & Codable> = UniqueElementsGraph<V, UnweightedEdge>
2020
public typealias WeightedUniqueElementsGraph<V: Equatable & Codable, W: Equatable & Codable> = UniqueElementsGraph<V, WeightedEdge<W>>
2121

22-
/// A subclass of UnweightedGraph that ensures there are no pairs of equal vertices and no repeated edges.
22+
/// An implementation Graph that ensures there are no pairs of equal vertices and no repeated edges.
2323
open class UniqueElementsGraph<V: Equatable & Codable, E: Edge & Equatable>: Graph {
2424
public var vertices: [V] = [V]()
2525
public var edges: [[E]] = [[E]]() //adjacency lists

Sources/SwiftGraph/UnweightedGraph.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// See the License for the specific language governing permissions and
1717
// limitations under the License.
1818

19-
/// A subclass of Graph with some convenience methods for adding and removing UnweightedEdges. WeightedEdges may be added to an UnweightedGraph but their weights will be ignored.
19+
/// An implementation of Graph with some convenience methods for adding and removing UnweightedEdges. WeightedEdges may be added to an UnweightedGraph but their weights will be ignored.
2020
open class UnweightedGraph<V: Equatable & Codable>: Graph {
2121
public var vertices: [V] = [V]()
2222
public var edges: [[UnweightedEdge]] = [[UnweightedEdge]]() //adjacency lists
@@ -29,6 +29,29 @@ open class UnweightedGraph<V: Equatable & Codable>: Graph {
2929
_ = self.addVertex(vertex)
3030
}
3131
}
32+
33+
/// Add an edge to the graph.
34+
///
35+
/// - parameter e: The edge to add.
36+
/// - parameter directed: If false, undirected edges are created.
37+
/// If true, a reversed edge is also created.
38+
/// Default is false.
39+
public func addEdge(_ e: UnweightedEdge, directed: Bool) {
40+
edges[e.u].append(e)
41+
if !directed && e.u != e.v {
42+
edges[e.v].append(e.reversed())
43+
}
44+
}
45+
46+
/// Add a vertex to the graph.
47+
///
48+
/// - parameter v: The vertex to be added.
49+
/// - returns: The index where the vertex was added.
50+
public func addVertex(_ v: V) -> Int {
51+
vertices.append(v)
52+
edges.append([E]())
53+
return vertices.count - 1
54+
}
3255
}
3356

3457
extension Graph where E == UnweightedEdge {

Sources/SwiftGraph/WeightedGraph.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
// See the License for the specific language governing permissions and
1717
// limitations under the License.
1818

19-
/// A subclass of Graph that has convenience methods for adding and removing WeightedEdges. All added Edges should have the same generic Comparable type W as the WeightedGraph itself.
19+
/// An implementation of Graph that has convenience methods for adding and removing WeightedEdges. All added Edges should have the same generic Comparable type W as the WeightedGraph itself.
2020
open class WeightedGraph<V: Equatable & Codable, W: Equatable & Codable>: Graph {
21+
2122
public var vertices: [V] = [V]()
2223
public var edges: [[WeightedEdge<W>]] = [[WeightedEdge<W>]]() //adjacency lists
2324

@@ -29,6 +30,29 @@ open class WeightedGraph<V: Equatable & Codable, W: Equatable & Codable>: Graph
2930
_ = self.addVertex(vertex)
3031
}
3132
}
33+
34+
/// Add an edge to the graph.
35+
///
36+
/// - parameter e: The edge to add.
37+
/// - parameter directed: If false, undirected edges are created.
38+
/// If true, a reversed edge is also created.
39+
/// Default is false.
40+
public func addEdge(_ e: WeightedEdge<W>, directed: Bool) {
41+
edges[e.u].append(e)
42+
if !directed && e.u != e.v {
43+
edges[e.v].append(e.reversed())
44+
}
45+
}
46+
47+
/// Add a vertex to the graph.
48+
///
49+
/// - parameter v: The vertex to be added.
50+
/// - returns: The index where the vertex was added.
51+
public func addVertex(_ v: V) -> Int {
52+
vertices.append(v)
53+
edges.append([E]())
54+
return vertices.count - 1
55+
}
3256
}
3357

3458
extension Graph where E: WeightedEdgeProtocol {

SwiftGraph.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'SwiftGraph'
3-
s.version = '2.0.0'
3+
s.version = '3.0.0'
44
s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
55
s.summary = 'A Graph Data Structure in Pure Swift'
66
s.homepage = 'https://github.com/davecom/SwiftGraph'

Tests/SwiftGraphTests/DijkstraGraphTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class DijkstraGraphTests: XCTestCase {
242242
}
243243

244244
func testRemovalWithDijkstra() {
245-
let cityGraph3 = cityGraph
245+
var cityGraph3 = cityGraph
246246
cityGraph3.removeVertex("Kansas City")
247247
let (distances, pathDict) = cityGraph3.dijkstra(root: "Miami", startDistance: 0)
248248
let nameDistance: [String: Int?] = distanceArrayToVertexDict(distances: distances, graph: cityGraph3)

0 commit comments

Comments
 (0)