Git Product home page Git Product logo

swiftgraph's Introduction

SwiftGraph

Swift Versions CocoaPods Version Carthage Compatible SPM Compatible CocoaPods Platforms Linux Compatible Twitter Contact Build Status Maintainability Test Coverage

SwiftGraph is a pure Swift (no Cocoa) implementation of a graph data structure, appropriate for use on all platforms Swift supports (iOS, macOS, Linux, etc.). It includes support for weighted, unweighted, directed, and undirected graphs. It uses generics to abstract away both the type of the vertices, and the type of the weights.

It includes copious in-source documentation, unit tests, as well as search functions for doing things like breadth-first search, depth-first search, and Dijkstra's algorithm. Further, it includes utility functions for topological sort, Jarnik's algorithm to find a minimum-spanning tree, detecting a DAG (directed-acyclic-graph), enumerating all cycles, and more.

Installation

SwiftGraph 3.0 and above requires Swift 5 (Xcode 10.2). Use SwiftGraph 2.0 for Swift 4.2 (Xcode 10.1) support, SwiftGraph 1.5.1 for Swift 4.1 (Xcode 9), SwiftGraph 1.4.1 for Swift 3 (Xcode 8), SwiftGraph 1.0.6 for Swift 2 (Xcode 7), and SwiftGraph 1.0.0 for Swift 1.2 (Xcode 6.3) support. SwiftGraph supports GNU/Linux and is tested on it.

CocoaPods

Use the CocoaPod SwiftGraph.

Carthage

Add the following to your Cartfile:

github "davecom/SwiftGraph" ~> 3.1

Swift Package Manager (SPM)

Use this repository as your dependency.

Manual

Copy all of the sources in the Sources folder into your project.

Tips and Tricks

  • To get a sense of how to use SwiftGraph, checkout the unit tests
  • Inserting an edge by vertex indices is much faster than inserting an edge by vertex objects that need to have their indices looked up
  • Generally, looking for the index of a vertex is O(n) time, with n being the number of vertices in the graph
  • SwiftGraph includes the functions bfs() and dfs() for finding a route between one vertex and another in a graph and dijkstra() for finding shortest paths in a weighted graph
  • A sample Mac app that implements the Nine Tails problem is included - just change the target of the project to SwiftGraphSampleApp to build it

Example

For more detail, checkout the Documentation section, but this example building up a weighted graph of American cities and doing some operations on it, should get you started.

let cityGraph: WeightedGraph<String, Int> = WeightedGraph<String, Int>(vertices: ["Seattle", "San Francisco", "Los Angeles", "Denver", "Kansas City", "Chicago", "Boston", "New York", "Atlanta", "Miami", "Dallas", "Houston"])

cityGraph is a WeightedGraph with String vertices and Int weights on its edges.

cityGraph.addEdge(from: "Seattle", to:"Chicago", weight:2097)
cityGraph.addEdge(from: "Seattle", to:"Chicago", weight:2097)
cityGraph.addEdge(from: "Seattle", to: "Denver", weight:1331)
cityGraph.addEdge(from: "Seattle", to: "San Francisco", weight:807)
cityGraph.addEdge(from: "San Francisco", to: "Denver", weight:1267)
cityGraph.addEdge(from: "San Francisco", to: "Los Angeles", weight:381)
cityGraph.addEdge(from: "Los Angeles", to: "Denver", weight:1015)
cityGraph.addEdge(from: "Los Angeles", to: "Kansas City", weight:1663)
cityGraph.addEdge(from: "Los Angeles", to: "Dallas", weight:1435)
cityGraph.addEdge(from: "Denver", to: "Chicago", weight:1003)
cityGraph.addEdge(from: "Denver", to: "Kansas City", weight:599)
cityGraph.addEdge(from: "Kansas City", to: "Chicago", weight:533)
cityGraph.addEdge(from: "Kansas City", to: "New York", weight:1260)
cityGraph.addEdge(from: "Kansas City", to: "Atlanta", weight:864)
cityGraph.addEdge(from: "Kansas City", to: "Dallas", weight:496)
cityGraph.addEdge(from: "Chicago", to: "Boston", weight:983)
cityGraph.addEdge(from: "Chicago", to: "New York", weight:787)
cityGraph.addEdge(from: "Boston", to: "New York", weight:214)
cityGraph.addEdge(from: "Atlanta", to: "New York", weight:888)
cityGraph.addEdge(from: "Atlanta", to: "Dallas", weight:781)
cityGraph.addEdge(from: "Atlanta", to: "Houston", weight:810)
cityGraph.addEdge(from: "Atlanta", to: "Miami", weight:661)
cityGraph.addEdge(from: "Houston", to: "Miami", weight:1187)
cityGraph.addEdge(from: "Houston", to: "Dallas", weight:239)

Convenience methods are used to add WeightedEdge connections between various vertices.

let (distances, pathDict) = cityGraph.dijkstra(root: "New York", startDistance: 0)
var nameDistance: [String: Int?] = distanceArrayToVertexDict(distances: distances, graph: cityGraph)
// shortest distance from New York to San Francisco
let temp = nameDistance["San Francisco"] 
// path between New York and San Francisco
let path: [WeightedEdge<Int>] = pathDictToPath(from: cityGraph.indexOfVertex("New York")!, to: cityGraph.indexOfVertex("San Francisco")!, pathDict: pathDict)
let stops: [String] = cityGraph.edgesToVertices(edges: path)

The shortest paths are found between various vertices in the graph using Dijkstra's algorithm.

let mst = cityGraph.mst()

The minimum spanning tree is found connecting all of the vertices in the graph.

let cycles = cityGraph.detectCycles()

All of the cycles in cityGraph are found.

let isADAG = cityGraph.isDAG

isADAG is false because cityGraph is not found to be a Directed Acyclic Graph.

let result = cityGraph.findAll(from: "New York") { v in
    return v.characters.first == "S"
}

A breadth-first search is performed, starting from New York, for all cities in cityGraph that start with the letter "S."

SwiftGraph contains many more useful features, but hopefully this example was a nice quickstart.

Documentation

There is a large amount of documentation in the source code using the latest Apple documentation technique—so you should be able to just alt-click a method name to get a lot of great information about it in Xcode. We also use Jazzy to produce HTML Docs. In addition, here's an overview of each of SwiftGraph's components:

Edges

Edges connect the vertices in your graph to one another.

  • 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 Edges must be Codable.
  • UnweightedEdge - This is a concrete implementation of Edge for unweighted graphs.
  • 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.

Graphs

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.

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:

for v in g {  // g is a Graph<String>
    print(v)
}

And we can grab a specific vertex by its index using a subscript

print(g[23]) // g is a Graph<String>

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.

  • 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 Graphs are Codable. Graph has methods for:
    • Adding a vertex
    • Getting the index of a vertex
    • Finding the neighbors of an index/vertex
    • Finding the edges of an index/vertex
    • Checking if an edge from one index/vertex to another index/vertex exists
    • Checking if a vertex is in the graph
    • Adding an edge
    • Removing all edges between two indexes/vertices
    • 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)
  • 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.
  • 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.
  • UniqueElementsGraph - a Graph implementation with support for union operations that ensures all vertices and edges in a graph are unique.

Search

Search methods are defined in extensions of Graph and WeightedGraph in Search.swift.

  • bfs() (method on Graph) - Finds a path from one vertex to another in a Graph using a breadth-first search. Returns an array of Edges 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().
  • dfs() (method on Graph) - Finds a path from one vertex to another in a Graph using a depth-first search. Returns an array of Edges 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().
  • 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.
  • 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.
  • Graph traversal versions of bfs() and dfs() that allow a visit function to execute at each stop

Sort & Miscellaneous

An extension to Graph in Sort.swift provides facilities for topological sort and detecting a DAG.

  • topologicalSort() - Does a topological sort of the vertices in a given graph if possible (returns nil if it finds a cycle). Returns a sorted list of the vertices. Runs in O(n) time.
  • isDAG - A property that uses topologicalSort() to determine whether a graph is a DAG (directed-acyclic graph). Runs in O(n) time.

An extension to WeightedGraph in MST.swift can find a minimum-spanning tree from a weighted graph.

  • mst() - Uses Jarnik's Algorithm (aka Prim's Algorithm) to find the tree made of minimum cumulative weight that connects all vertices in a weighted graph. This assumes the graph is completely undirected and connected. If the graph has directed edges, it may not return the right answer. Also, if the graph is not fully connected it will create the tree for the connected component that the starting vertex is a part of. Returns an array of WeightedEdges that compose the tree. Use utility functions totalWeight() and printMST() to examine the returned MST. Runs in O(n lg n) time.

An extension to Graph in Cycles.swift finds all of the cycles in a graph.

  • 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.

An extension to Graph in Reversed.swift reverses all of the edges in a graph.

Authorship, License, & Contributors

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.

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.

If you use SwiftGraph in your product, please let me know by getting in touch with me. It's just cool to know.

Future Direction

Future directions for this project to take could include:

  • More utility functions
  • A thread safe implementation of Graph
  • More extensive performance testing
  • GraphML and Other Format Support

swiftgraph's People

Contributors

dabbott avatar davecom avatar ferranpujolcamins avatar klundberg avatar macbellingrath avatar mattpaletta avatar peterkos avatar zeveisenberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftgraph's Issues

Use the generated xcodeproj and test boilerplate

What are the reasons to add the xcodeproj in git?

My proposal is to remove it from git and just let swift package generate one whenever is needed by a user. Otherwise it's difficult to add a dependency in Package.swift

Also, swift test can generate the testing boilerplate for linux, I also propose to use it.

Swift 5 Support

Check all is well on Swift 5 after the final release of it ships with Xcode 10.2 and if any changes are needed. Review the changes in recent pull requests by @ZevEisenberg and @ferranpujolcamins .

Development for Swift 5/SwiftGraph 2.1 is taking place in the release2.1/swift5 branch.

Add NSCoding Support

By having a pure Swift implementation of a Graph, there's currently no way, as far as I can tell, to write a Graph to disk with NSKeyedArchiving / NSCoding.

Parallelize cycle finding

Would it be possible to speed up detectCycles and detectCyclesAsEdges by making them run parts of the search in parallel? I don't know whether the algorithm is suitable for parallelization, but it could be worth looking into.

Background: I've been modifying my juggling code to deal with throwing multiple objects at the same time, and the state graphs for this technique are way bigger than for throwing a single object at once. For 3 balls, for a maximum throw height of 6, there are 110,552 cycles in the graph. I don't know how many there are for a maximum throw height of 7 because I haven't been able to run the code for long enough yet to find out.

For graphs this large, memory usage is also a concern. My current test run searching for a max throw height of 7 is up over 1 GB used.

(Much of this is academic. I don't know if I have a real-world use case for searching for cycles on graphs this large, at least not quickly. If my app needs to use them, I'll probably generate them once and bake them into the app.)

Implement Graph Union operation

I'm interested in contributing graph union operation: http://mathworld.wolfram.com/GraphUnion.html
It should be quite straightforward for unweighted graphs. For weighted graphs, we need to add different ways to handle weight conflicts on common edges:

  • Built in "reducers": sum, average, min, max.
  • Support custom reducers.
  • Report conflicts as exceptions or errors (this could be modeled as a reducer too)

My only question is how to compute the disjoint set union. You store edges and nodes as Array. Is building a Set from one array, use Set.formUnion and then convert back to array a good solution? I can't find info on computational complexity for these operations.

PS: maybe it makes more sense for the weighted case to just treat edges between same nodes as distinct edges. Then add another operation that merges edges between common nodes.

GraphML support

Is it present/planned? If not I would be interested in adding it.

Changelog File

Please add a CHANGELOG.md file to make it easier to track recent changes in the framework.

Index out of bounds crash removing vertex

I'm getting an array index out of bounds crash when removing a vertex, the crash is in Graph.swift line 207 and 226. It seems strange that it could crash there so I modified the function a bit to collect some more data by checking the index and printing out out some more data.

The modified loop in the function looks like:

        for j in 0..<index {
            print("Removing Vertices")
            let beginningCount = edges[j].count
            var toRemove: [Int] = [Int]()
            for l in 0..<edges[j].count {
                if edges[j][l].v == index {
                    toRemove.append(l)
                    continue
                }
                if edges[j][l].v > index {
                    edges[j][l].v -= 1
                }
            }
            for f in toRemove {
                
                if f < edges[j].count {
                    edges[j].remove(at: f)
                }
                else {
                    print("Number Of edges: \(edges[j].count), starting count: \(beginningCount) , toRemove: \(f)")
                }
            }
        }

When I run it I get things in the console like:

Removing Vertices
Number Of edges: 91, starting count: 144 , toRemove: 91
Number Of edges: 91, starting count: 144 , toRemove: 92
Number Of edges: 91, starting count: 144 , toRemove: 93
Number Of edges: 91, starting count: 144 , toRemove: 94
Number Of edges: 91, starting count: 144 , toRemove: 95
Number Of edges: 91, starting count: 144 , toRemove: 96
Number Of edges: 91, starting count: 144 , toRemove: 97
Number Of edges: 91, starting count: 144 , toRemove: 98
Number Of edges: 91, starting count: 144 , toRemove: 99
Number Of edges: 91, starting count: 144 , toRemove: 100
Number Of edges: 91, starting count: 144 , toRemove: 101
Number Of edges: 91, starting count: 144 , toRemove: 102

It seems as if the array is being modified from the beginning of the loop when it has 144 elements and the array of vertices to be removed is being calculate, to the next step when the vertices are being removed, but I can't see where the modification is being made.

Make graph classes structs?

Since now Graph is a protocol, we don't use inheritance that much. The only class that is still inheriting is UniqueElementsGraph. Shall we convert all the graph classes to structs?
UnweightedGraph and WeightedGraph store the graph in a couple of arrays, which are value types. So it makes sense for the graph classes to also be value types from this point of view.
This will also allow us to remove the Codable specific classes and implement it in an extension right?

Experiment with Refactoring Graph from Class to Protocol

In the original version of SwiftGraph, developed under Swift 1, Swift did not support protocol extensions (function implementations in protocols) so it was not particularly useful to make Graph a protocol. I need to see how much existing code would be broken by refactoring Graph into a protocol. Since this would be a large breaking change, it would only be made permanent for version 2.0 of SwiftGraph.

Running BFS/DFS and returning the names associated with edges

Hello,
I have string vertices in a weighted undirected graph. I created edges doing AddEdge(from: "name1", to: "name2", weight: xx). When i run BFS/DFS and print the list of edges i get back, the u and v fields are ints. I assume this is the index of the vertices. Is there a way to get the string name associated with the vertex at this point?
Thanks.

removeAllEdges crash

Calling
public func removeAllEdges(from: V, to: V, bidirectional: Bool = true)
crashes at calling
public func removeAllEdges(from: Int, to: Int, bidirectional: Bool = true)
with fatal error: Index out of range

Trouble Pushing 1.1.0 to CocoaPods

CocoaPods 1.0.1 with Xcode 8 Beta 3 seems to be rebasing the git repository for some bizarre reason:

Davids-Air:swiftgraph dave$ pod trunk push SwiftGraph.podspec
Updating spec repo `master`

CocoaPods 1.1.0.beta.1 is available.
To update use: `sudo gem install cocoapods --pre`
[!] This is a test version we'd love you to try.

For more information, see https://blog.cocoapods.org and the CHANGELOG for this version at https://github.com/CocoaPods/CocoaPods/releases/tag/1.1.0.beta.1

Validating podspec
 -> SwiftGraph (1.1.0)
    - ERROR | [iOS] unknown: Encountered an unknown error (Must be in the root of the repo (/Users/dave/.cocoapods/repos/master), instead in /Users/dave/Documents/Projects/SwiftGraph.) during validation.

[!] The spec did not pass validation, due to 1 error.

Find New HTML Docs Provider

CocoaPods has stopped its documentation service. We could generate our own docs again on GitHub pages or find another automated service.

Doesn't Build Under Xcode 7.3

Unfortunately the function anyGenerator() (lowercase a) used in the SequenceType implementation has been deprecated and replaced with AnyGenerator() in Swift 2.2 causing the build issue.

Spruce Up README

Add an extended example to the homepage of doing useful things with SwiftGraph and add badges to show Linux support, Swift 4 support, it's building, etc.

Type `Graph<V>` does not conform to protocol `IndexableBase`

Hello.

I was trying SwiftGraph with Swift 3 and I'm getting the following error:

Type Graph<V> does not conform to protocol IndexableBase for the below declaration inside Graph.swift:

public class Graph<V: Equatable>: CustomStringConvertible, Swift.Sequence, Swift.Collection { ... }

Any leads? Thanks.

Graphs with Int Vertices have Ambiguous Method Calls

Is there a reason to make the convenience methods public?
I've had a small issue when attempting to create graphs with them, as sometimes they work and sometimes they don't (depending on the type used).

For instance, take the following graph declaration:

var graph:UnweightedGraph<String> = UnweightedGraph<String>()

graph.addVertex("Hello")
graph.addVertex("World")
graph.addEdge("Hello", to: "World")

print(graph.description)

This prints the graph correctly.
However, when replacing the graph type from String to Int, the method (and the other convenience methods for addEdge) throws an ambiguous use error.

var graph2: UnweightedGraph<Int> = UnweightedGraph<Int>()

graph2.addVertex(123)
graph2.addVertex(456)
graph2.addEdge(0, to: 1, directed: false)        // Throws ambiguous use error
graph2.addEdge(0, to: 1)                         // Throws ambiguous use error
graph2.addEdge(UnweightedEdge.init(u: 0, v: 1, directed: false)) // Works fine (as intended)

print(graph2.description)

Make `Graph` conform to `Codable` only when the vertices are `Codable`?

Hi,

Native swift types like Array only conform to Codable when their elements are Codable.
It looks like Graph doesn't have any special need for being Codable.
Would it be possible to make Graph conform to Codable only when its vertices are? I've a use case where I'm not interested in the Graph being codable and adding conformance to the vertex type would be a lot of work.

Comparison of `WeightedEdge` should take into consideration whether the edges are directed

Let's say I have two weighted and not directed edges, connecting the same vertices, as follows:

let edgeA = WeightedEdge<Int> = WeightedEdge(u: 0, v: 1, directed: false, weight: 10)
let edgeB = WeightedEdge<Int> = WeightedEdge(u: 1, v: 0, directed: false, weight: 10)

Currently we have so that edgeA == edgeB returns false.

Shouldn't the ==(_:_:) operator take into consideration whether they're directed? Or am I missing something?
So in this particular scenario I would expect edgeA == edgeB to return true, as they are not directed.


In summary we would have:

let directedEdgeA = WeightedEdge<Int> = WeightedEdge(u: 0, v: 1, directed: false, weight: 10)
let directedEdgeB = WeightedEdge<Int> = WeightedEdge(u: 1, v: 0, directed: false, weight: 10)
directedEdgeA == directedEdgeB // returns `true`


let undirectedEdgeC = WeightedEdge<Int> = WeightedEdge(u: 0, v: 1, directed: true, weight: 10)
let undirectedEdgeD = WeightedEdge<Int> = WeightedEdge(u: 1, v: 0, directed: true, weight: 10)
undirectedEdgeC == undirectedEdgeD // returns `false`

If there are any worries about how changing the ==(_:_:) implementation would affect other features, should we have a separate method to compare two edges?
Any thoughts on this?

Problem with neighborsForVertex

Hi,
I tried to use this library for PERT https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique

i have something like this:
Graph
code:

    let A = Activity(a: 1,m: 2,b: 3, name: "A")
    let B = Activity(a: 2,m: 3,b: 4, name: "B")
    let C = Activity(a: 1, m: 2, b: 3, name: "C")
    let D = Activity(a: 1, m: 2, b: 3, name: "D")
    let E = Activity(a: 3, m: 4, b: 5, name: "E")
    let F = Activity(a: 2, m: 4, b: 6, name: "F")
    let G = Activity(a: 1, m: 3, b: 5, name: "G")
    let H = Activity(a: 3, m: 5, b: 7, name: "H")
    let I = Activity(a: 5, m: 7, b: 9, name: "I")
    
    let graph = UnweightedGraph<Activity>(vertices: [A,B,C,D,E,F,G,H,I])
    graph.addEdge(from: A, to: C, directed: true)
    graph.addEdge(from: A, to: D, directed: true)
    graph.addEdge(from: B, to: E, directed: true)
    graph.addEdge(from: D, to: F, directed: true)
    graph.addEdge(from: E, to: F, directed: true)
    graph.addEdge(from: C, to: G, directed: true)
    graph.addEdge(from: C, to: H, directed: true)
    graph.addEdge(from: F, to: I, directed: true)
    graph.addEdge(from: H, to: I, directed: true)
    
    print(graph.neighborsForVertex(E) ?? [])

struct Activity {
    let a: Int
    let m: Int
    let b: Int
    let name: String
}

extension Activity: Equatable {
    
    func estimationTime() -> Double {
        let time  = a + 4 * m + b
        return Double(time) / 6.0
    }
    
    func standardVariation() -> Double {
        let time = b - a
        return Double(time) / 6.0
    }
    
    //MARK: Equatable
    static public func ==(lhs: Activity, rhs: Activity) -> Bool {
        return lhs.estimationTime() == rhs.estimationTime()
    }
}

neightbors for E prints me something like:
[PERT.Activity(a: 3, m: 4, b: 5, name: "E"), PERT.Activity(a: 5, m: 7, b: 9, name: "I")]

Maybe i missunderstood something, but i think its not working correctly

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.