Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
6 changes: 0 additions & 6 deletions Graphs/Johnson's algorithm - Swift/readme.rtf

This file was deleted.

28 changes: 28 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "JohnsonsAlgo",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "JohnsonsAlgo",
targets: ["JohnsonsAlgo"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "JohnsonsAlgo",
dependencies: []),
.testTarget(
name: "JohnsonsAlgoTests",
dependencies: ["JohnsonsAlgo"]),
]
)
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# JohnsonsAlgo

Swift library that implements the Jonhson Cycle algorithm to find circuits in a directed graph
Wikipedia: "https://en.wikipedia.org/wiki/Johnson's_algorithm"

Original Author: Frank Meyer (Java implementation)
Swift Adaption: Marc Matta
Swift Package Manager Adaption: Klaus Kneupner

Known issues: sometimes the algo will end in a recursive loop, depending on the sequence of nodes provided. Details yet unknown.


Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import Foundation

public typealias AdjacencyMatrix = Array<Array<Bool>>
//public typealias AdjacencyList = Array<Array<Int>>

/**
* Calculates the adjacency-list for a given adjacency-matrix.
*
Expand All @@ -16,7 +19,7 @@ import Foundation
* @version 1.0, 26.08.2006
*
*/
@objc public class AdjacencyList : NSObject {
public class AdjacencyList {
/**
* Calculates a adjacency-list for a given array of an adjacency-matrix.
*
Expand All @@ -28,23 +31,23 @@ import Foundation
* that are direct successornodes of the node.
*/
class func getAdjacencyList( adjacencyMatrix: Array<Array<Bool>>) -> Array<Array<Int>> {
var list = [Array<Int>](count: adjacencyMatrix.count, repeatedValue: [])
var list = [Array<Int>](repeating: [], count: adjacencyMatrix.count)

for var i = 0; i < adjacencyMatrix.count; ++i {
for i in 0..<adjacencyMatrix.count {
var v = [Int]()
for var j = 0; j < adjacencyMatrix[i].count; ++j {
for j in 0..<adjacencyMatrix[i].count{
if (adjacencyMatrix[i][j]) {
v.append(j)
}
}

list[i] = [Int](count:v.count, repeatedValue: 0)
for var j = 0; j < v.count; ++j {
list[i] = [Int](repeating: 0, count:v.count)
for j in 0..<v.count {
let in_ = v[j]
list[i][j] = in_
}
}

return list;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

import Foundation

public typealias Matrix<Element> = Array<Array<Element>>

public enum GraphErrors: Error {
case algoIssue
case missingObject
}

/**
* Searchs all elementary cycles in a given directed graph. The implementation
* is independent from the concrete objects that represent the graphnodes, it
Expand All @@ -33,15 +40,15 @@ import Foundation
*
*/

@objc public class ElementaryCyclesSearch : NSObject {
open class ElementaryCyclesSearch<Element> {
/** List of cycles */
private var cycles : Array<Array<AnyObject>>?
private var cycles : Array<Array<Element>>?

/** Adjacency-list of graph */
private var adjList : Array<Array<Int>>

/** Graphnodes */
private var graphNodes : Array<AnyObject>
private var graphNodes : Array<Element>

/** Blocked nodes, used by the algorithm of Johnson */
private var blocked : Array<Bool>?
Expand All @@ -60,9 +67,9 @@ import Foundation
* build sets of the elementary cycles containing the objects of the original
* graph-representation
*/
init(matrix: Array<Array<Bool>>, graphNodes: Array<AnyObject>) {
public init(matrix: Array<Array<Bool>>, graphNodes: Array<Element>) {
self.graphNodes = graphNodes;
self.adjList = AdjacencyList.getAdjacencyList(matrix);
self.adjList = AdjacencyList.getAdjacencyList(adjacencyMatrix: matrix);
}

/**
Expand All @@ -71,27 +78,30 @@ import Foundation
*
* @return List::List::Object with the Lists of the elementary cycles.
*/
func getElementaryCycles() -> Array<Array<AnyObject>> {
self.cycles = Array<Array<AnyObject>>()
self.blocked = [Bool](count:self.adjList.count, repeatedValue:false)
self.B = [Array<Int>](count: self.adjList.count, repeatedValue: [])
open func getElementaryCycles() throws -> Array<Array<Element>> {
if let result = cycles {
return result
}
self.cycles = Array<Array<Element>>()
self.blocked = [Bool](repeating:false, count:self.adjList.count)
self.B = [Array<Int>](repeating: [], count: self.adjList.count)
self.stack = [Int]()

let sccs = StrongConnectedComponents(adjList: self.adjList)
var s = 0

while(true) {
let sccResult = sccs.getAdjacencyList(s)
let sccResult = sccs.getAdjacencyList(node: s)
if let result = sccResult {
let scc = result.adjList
s = result.lowestNodeId
for var j = 0; j<scc.count; ++j {
for j in 0..<scc.count{
self.blocked![j] = false
self.B![j] = [Int]()
}

findCycles(s, s: s, adjList: scc)
s++
let _ = try findCycles(v: s, s: s, adjList: scc)
s = s + 1
}else {
break
}
Expand All @@ -110,43 +120,49 @@ import Foundation
* connected component s is part of.
* @return true, if cycle found; false otherwise
*/
func findCycles(v: Int, s: Int, adjList:Array<Array<Int>> ) -> Bool {
func findCycles(v: Int, s: Int, adjList:Array<Array<Int>> ) throws -> Bool {
var f = false;


if stack?.contains(v) ?? false {
self.cycles = Array<Array<Element>>()
throw GraphErrors.algoIssue
}
self.stack?.append(v)
self.blocked![v] = true;

for var i = 0; i < adjList[v].count; ++i {
for i in 0..<adjList[v].count {
let w = adjList[v][i]
// found cycle
if (w == s) {
var cycle = Array<AnyObject>()
for var j = 0; j < self.stack!.count; ++j {
var cycle = Array<Element>()
for j in 0..<self.stack!.count {
let index = self.stack![j]
cycle.append(self.graphNodes[index])
}
self.cycles?.append(cycle);
f = true;
} else if (!self.blocked![w]) {
if (findCycles(w, s: s, adjList: adjList)) {
if (try findCycles(v: w, s: s, adjList: adjList)) {
f = true;
}
}
}

if (f) {
unblock(v)
unblock(node: v)
} else {
for var i = 0; i < adjList[v].count; ++i {
for i in 0..<adjList[v].count {
let w = adjList[v][i]
if (!self.B![w].contains(v)) {
self.B![w].append(v)
}
}
}

let indexToRemove = self.stack!.indexOf(v)
let indexToRemove = self.stack!.firstIndex(of: v)
if let index = indexToRemove {
self.stack!.removeAtIndex(index)
self.stack?.remove(at: index)
}
return f
}
Expand All @@ -161,10 +177,10 @@ import Foundation
var Bnode = self.B![node]
while (Bnode.count > 0) {
let w = Bnode.first!
Bnode.removeAtIndex(0)
Bnode.remove(at:0)
if (self.blocked![w]) {
self.unblock(w)
self.unblock(node: w)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

@objc public class SCCResult : NSObject{
public class SCCResult {
var nodeIDsOfSCC : Set<Int>
var adjList : Array<Array<Int>>
var lowestNodeId : Int
Expand All @@ -17,10 +17,10 @@ import Foundation
self.adjList = adjList
self.lowestNodeId = lowestNodeId
nodeIDsOfSCC = []
for var index = lowestNodeId; index < adjList.count; ++index {
for index in lowestNodeId..<adjList.count {
if (adjList[index].count > 0) {
self.nodeIDsOfSCC.insert(index)
}
}
}
}
}
Loading