From f1fad968edf711a42610a5a868f212819fa8b0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sun, 25 Jan 2015 22:32:54 +0100 Subject: [PATCH 1/2] Remove Algorithm namespace (split off into separate repository) --- lib/Fhaculty/Graph/Algorithm/Base.php | 5 - lib/Fhaculty/Graph/Algorithm/BaseDual.php | 33 -- lib/Fhaculty/Graph/Algorithm/BaseGraph.php | 29 -- lib/Fhaculty/Graph/Algorithm/BaseVertex.php | 29 -- lib/Fhaculty/Graph/Algorithm/Bipartit.php | 122 ------ lib/Fhaculty/Graph/Algorithm/Complete.php | 43 -- .../Graph/Algorithm/ConnectedComponents.php | 151 ------- lib/Fhaculty/Graph/Algorithm/Degree.php | 220 ---------- .../Graph/Algorithm/DetectNegativeCycle.php | 82 ---- lib/Fhaculty/Graph/Algorithm/Directed.php | 66 --- lib/Fhaculty/Graph/Algorithm/Eulerian.php | 38 -- lib/Fhaculty/Graph/Algorithm/Flow.php | 118 ------ lib/Fhaculty/Graph/Algorithm/Groups.php | 87 ---- lib/Fhaculty/Graph/Algorithm/Loop.php | 52 --- .../Graph/Algorithm/MaxFlow/EdmondsKarp.php | 129 ------ .../Graph/Algorithm/MaximumMatching/Base.php | 42 -- .../Graph/Algorithm/MaximumMatching/Flow.php | 87 ---- .../Graph/Algorithm/MinimumCostFlow/Base.php | 83 ---- .../MinimumCostFlow/CycleCanceling.php | 79 ---- .../SuccessiveShortestPath.php | 161 -------- .../Algorithm/MinimumSpanningTree/Base.php | 91 ----- .../Algorithm/MinimumSpanningTree/Kruskal.php | 134 ------ .../Algorithm/MinimumSpanningTree/Prim.php | 81 ---- lib/Fhaculty/Graph/Algorithm/Parallel.php | 84 ---- .../Algorithm/Property/GraphProperty.php | 51 --- .../Graph/Algorithm/Property/WalkProperty.php | 381 ------------------ .../Graph/Algorithm/ResidualGraph.php | 109 ----- lib/Fhaculty/Graph/Algorithm/Search/Base.php | 56 --- .../Graph/Algorithm/Search/BreadthFirst.php | 45 --- .../Graph/Algorithm/Search/DepthFirst.php | 66 --- .../Graph/Algorithm/ShortestPath/Base.php | 287 ------------- .../Algorithm/ShortestPath/BreadthFirst.php | 132 ------ .../Graph/Algorithm/ShortestPath/Dijkstra.php | 121 ------ .../ShortestPath/MooreBellmanFord.php | 123 ------ lib/Fhaculty/Graph/Algorithm/Symmetric.php | 44 -- .../Graph/Algorithm/TopologicalSort.php | 65 --- .../Graph/Algorithm/TransposeGraph.php | 36 -- .../TravelingSalesmanProblem/Base.php | 70 ---- .../TravelingSalesmanProblem/Bruteforce.php | 215 ---------- .../MinimumSpanningTree.php | 74 ---- .../NearestNeighbor.php | 93 ----- lib/Fhaculty/Graph/Algorithm/Tree/Base.php | 107 ----- .../Graph/Algorithm/Tree/BaseDirected.php | 325 --------------- lib/Fhaculty/Graph/Algorithm/Tree/InTree.php | 44 -- lib/Fhaculty/Graph/Algorithm/Tree/OutTree.php | 44 -- .../Graph/Algorithm/Tree/Undirected.php | 150 ------- lib/Fhaculty/Graph/Algorithm/Weight.php | 107 ----- .../Fhaculty/Graph/Algorithm/BipartitTest.php | 94 ----- .../Fhaculty/Graph/Algorithm/CompleteTest.php | 65 --- .../Algorithm/ConnectedComponentsTest.php | 97 ----- tests/Fhaculty/Graph/Algorithm/DegreeTest.php | 100 ----- .../Algorithm/DetectNegativeCycleTest.php | 151 ------- .../Fhaculty/Graph/Algorithm/DirectedTest.php | 58 --- .../Fhaculty/Graph/Algorithm/EulerianTest.php | 45 --- tests/Fhaculty/Graph/Algorithm/FlowTest.php | 98 ----- tests/Fhaculty/Graph/Algorithm/GroupsTest.php | 62 --- tests/Fhaculty/Graph/Algorithm/LoopTest.php | 57 --- .../Algorithm/MaxFlow/EdmondsKarpTest.php | 169 -------- .../Algorithm/MaximumMatching/FlowTest.php | 65 --- .../Algorithm/MinimumCostFlow/BaseMcfTest.php | 203 ---------- .../MinimumCostFlow/CycleCancellingTest.php | 12 - .../SuccessiveShortestPathTest.php | 12 - .../MinimumSpanningTree/BaseMstTest.php | 169 -------- .../MinimumSpanningTree/KruskalTest.php | 24 -- .../MinimumSpanningTree/PrimTest.php | 12 - .../Fhaculty/Graph/Algorithm/ParallelTest.php | 97 ----- .../Algorithm/Property/PropertyGraphTest.php | 30 -- .../Algorithm/Property/WalkPropertyTest.php | 186 --------- .../Graph/Algorithm/ResidualGraphTest.php | 133 ------ .../ShortestPath/BaseShortestPathTest.php | 169 -------- .../ShortestPath/BreadthFirstTest.php | 38 -- .../Algorithm/ShortestPath/DijkstraTest.php | 31 -- .../ShortestPath/MooreBellmanFordTest.php | 96 ----- .../Graph/Algorithm/SymmetricTest.php | 61 --- .../Graph/Algorithm/TopologicalSortTest.php | 77 ---- .../Graph/Algorithm/Tree/BaseDirectedTest.php | 199 --------- .../Graph/Algorithm/Tree/InTreeTest.php | 48 --- .../Graph/Algorithm/Tree/OutTreeTest.php | 48 --- .../Graph/Algorithm/Tree/UndirectedTest.php | 114 ------ tests/Fhaculty/Graph/Algorithm/WeightTest.php | 58 --- .../Graph/Loader/CompleteGraphTest.php | 11 - 81 files changed, 7680 deletions(-) delete mode 100644 lib/Fhaculty/Graph/Algorithm/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/BaseDual.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/BaseGraph.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/BaseVertex.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Bipartit.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Complete.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ConnectedComponents.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Degree.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/DetectNegativeCycle.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Directed.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Eulerian.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Flow.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Groups.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Loop.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarp.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MaximumMatching/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MaximumMatching/Flow.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCanceling.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPath.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Kruskal.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Prim.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Parallel.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Property/GraphProperty.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Property/WalkProperty.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ResidualGraph.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Search/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Search/BreadthFirst.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Search/DepthFirst.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ShortestPath/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirst.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ShortestPath/Dijkstra.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFord.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Symmetric.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TopologicalSort.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TransposeGraph.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Bruteforce.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/MinimumSpanningTree.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/NearestNeighbor.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Tree/Base.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Tree/BaseDirected.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Tree/InTree.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Tree/OutTree.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Tree/Undirected.php delete mode 100644 lib/Fhaculty/Graph/Algorithm/Weight.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/BipartitTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/CompleteTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ConnectedComponentsTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/DegreeTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/DetectNegativeCycleTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/DirectedTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/EulerianTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/FlowTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/GroupsTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/LoopTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarpTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MaximumMatching/FlowTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/BaseMcfTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCancellingTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPathTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/BaseMstTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/KruskalTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/PrimTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ParallelTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Property/PropertyGraphTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Property/WalkPropertyTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ResidualGraphTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ShortestPath/BaseShortestPathTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirstTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ShortestPath/DijkstraTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFordTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/SymmetricTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/TopologicalSortTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Tree/BaseDirectedTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Tree/InTreeTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Tree/OutTreeTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/Tree/UndirectedTest.php delete mode 100644 tests/Fhaculty/Graph/Algorithm/WeightTest.php diff --git a/lib/Fhaculty/Graph/Algorithm/Base.php b/lib/Fhaculty/Graph/Algorithm/Base.php deleted file mode 100644 index b4243e20..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Base.php +++ /dev/null @@ -1,5 +0,0 @@ -set = $graphOrWalk; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/BaseGraph.php b/lib/Fhaculty/Graph/Algorithm/BaseGraph.php deleted file mode 100644 index ab2e0853..00000000 --- a/lib/Fhaculty/Graph/Algorithm/BaseGraph.php +++ /dev/null @@ -1,29 +0,0 @@ -graph = $graph; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/BaseVertex.php b/lib/Fhaculty/Graph/Algorithm/BaseVertex.php deleted file mode 100644 index 846ebe4c..00000000 --- a/lib/Fhaculty/Graph/Algorithm/BaseVertex.php +++ /dev/null @@ -1,29 +0,0 @@ -vertex = $vertex; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Bipartit.php b/lib/Fhaculty/Graph/Algorithm/Bipartit.php deleted file mode 100644 index a62f5c7d..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Bipartit.php +++ /dev/null @@ -1,122 +0,0 @@ -getColors(); - - return true; - } catch (UnexpectedValueException $ignore) { } - - return false; - } - - /** - * checks whether the input graph's vertex groups are a valid bipartition - * - * @return boolean - * @uses AlgorithmGroups::isBipartit() - */ - public function isBipartitGroups() - { - $alg = new Groups($this->graph); - - return $alg->isBipartit(); - } - - /** - * get map of vertex ID to vertex color - * - * @return int[] - * @throws UnexpectedValueException if graph is not bipartit - * @uses AlgorithmBipartit::checkVertex() for every vertex not already colored - */ - public function getColors() - { - $colors = array(); - - // get color for each vertex - foreach ($this->graph->getVertices()->getMap() as $vid => $startVertex) { - if (!isset($colors[$vid])) { - $queue = array($startVertex); - // initialize each components color - $colors[$vid] = 0; - - // breadth search all vertices in same component - do { - // next vertex in color - $vertex = array_shift($queue); - $color = $colors[$vertex->getId()]; - $nextColor = 1-$color; - - // scan all vertices connected to this vertex - foreach ($vertex->getVerticesEdge()->getMap() as $vid => $nextVertex) { - // color unknown, so expect next color for this vertex - if (!isset($colors[$vid])) { - $colors[$vid] = $nextColor; - $queue []= $nextVertex; - // color is known but differs => can not be bipartit - } elseif ($colors[$vid] !== $nextColor) { - throw new UnexpectedValueException('Graph is not bipartit'); - } - } - } while ($queue); - } - } - - return $colors; - } - - /** - * get groups of vertices per color - * - * @return array[] array of arrays of vertices - */ - public function getColorVertices() - { - $colors = $this->getColors(); - $ret = array(0 => array(), 1 => array()); - - foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) { - $ret[$colors[$vid]][$vid] = $vertex; - } - - return $ret; - } - - /** - * create new graph with valid groups set according to bipartition colors - * - * @return Graph - * @throws UnexpectedValueException if graph is not bipartit - * @uses AlgorithmBipartit::getColors() - * @uses Graph::createGraphClone() - * @uses Vertex::setGroup() - */ - public function createGraphGroups() - { - $colors = $this->getColors(); - - $graph = $this->graph->createGraphClone(); - foreach ($graph->getVertices()->getMap() as $vid => $vertex) { - $vertex->setGroup($colors[$vid]); - } - - return $graph; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Complete.php b/lib/Fhaculty/Graph/Algorithm/Complete.php deleted file mode 100644 index ba4dc681..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Complete.php +++ /dev/null @@ -1,43 +0,0 @@ -graph->getVertices()->getVector(); - // from each vertex - foreach ($vertices as $vertex) { - // to each vertex - foreach ($c as $other) { - // missing edge => fail - if ($other !== $vertex && !$vertex->hasEdgeTo($other)) { - return false; - } - } - } - - return true; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/ConnectedComponents.php b/lib/Fhaculty/Graph/Algorithm/ConnectedComponents.php deleted file mode 100644 index 8b2c6463..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ConnectedComponents.php +++ /dev/null @@ -1,151 +0,0 @@ -getGraph() !== $this->graph) { - throw new InvalidArgumentException('This graph does not contain the given vertex'); - } - - return $this->graph->createGraphCloneVertices($this->createSearch($vertex)->getVertices()); - } - - /** - * - * @param Vertex $vertex - * @return SearchBreadthFirst - */ - private function createSearch(Vertex $vertex) - { - $alg = new SearchBreadthFirst($vertex); - - // follow into both directions (loosely connected) - return $alg->setDirection(SearchBreadthFirst::DIRECTION_BOTH); - } - - /** - * check whether this graph consists of only a single component - * - * If a Graph consists of only a single component, it is said to be a - * connected Graph, otherwise it's called a disconnected Graph. - * - * This method returns exactly the same result as checking - *
($this->getNumberOfComponents() === 1)
. However, using this - * method is faster than calling getNumberOfComponents(), as it only has to - * count all vertices in one component to see if the graph consists of only - * a single component. - * - * As such, a null Graph (a Graph with no vertices) is not considered - * connected here. - * - * @return boolean - * @see self::getNumberOfComponents() - */ - public function isSingle() - { - try { - $vertex = $this->graph->getVertices()->getVertexFirst(); - } - catch (UnderflowException $e) { - // no first vertex => empty graph => has zero components - return false; - } - $alg = $this->createSearch($vertex); - - return (count($this->graph->getVertices()) === count($alg->getVertices())); - } - - /** - * count number of connected components - * - * A null Graph (a Graph with no vertices) will return 0 components. - * - * @return int number of components - * @uses Graph::getVertices() - * @uses AlgorithmSearchBreadthFirst::getVertices() - */ - public function getNumberOfComponents() - { - $visitedVertices = array(); - $components = 0; - - // for each vertices - foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) { - // did I visit this vertex before? - if (!isset($visitedVertices[$vid])) { - - // get all vertices of this component - $newVertices = $this->createSearch($vertex)->getVertices()->getIds(); - - ++$components; - - // mark the vertices of this component as visited - foreach ($newVertices as $vid) { - $visitedVertices[$vid] = true; - } - } - } - - // return number of components - return $components; - } - - /** - * separate input graph into separate independant and unconnected graphs - * - * @return Graph[] - * @uses Graph::getVertices() - * @uses AlgorithmSearchBreadthFirst::getVertices() - */ - public function createGraphsComponents() - { - $visitedVertices = array(); - $graphs = array(); - - // for each vertices - foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) { - // did I visit this vertex before? - if (!isset($visitedVertices[$vid])) { - - $alg = $this->createSearch($vertex); - // get all vertices of this component - $newVertices = $alg->getVertices(); - - // mark the vertices of this component as visited - foreach ($newVertices->getIds() as $vid) { - $visitedVertices[$vid] = true; - } - - $graphs []= $this->graph->createGraphCloneVertices($newVertices); - } - } - - return $graphs; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Degree.php b/lib/Fhaculty/Graph/Algorithm/Degree.php deleted file mode 100644 index 7ca1f34c..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Degree.php +++ /dev/null @@ -1,220 +0,0 @@ -getDegreeVertex($this->graph->getVertices()->getVertexFirst()); - - foreach ($this->graph->getVertices() as $vertex) { - /** @var $vertex Vertex */ - $i = $this->getDegreeVertex($vertex); - - if ($i !== $degree) { - throw new UnexpectedValueException('Graph is not k-regular (vertex degrees differ)'); - } - } - - return $degree; - } - - /** - * get minimum degree of vertices - * - * @return int - * @throws Exception if graph is empty or directed - * @uses Vertices::getVertexOrder() - * @uses self::getDegreeVertex() - */ - public function getDegreeMin() - { - return $this->getDegreeVertex($this->graph->getVertices()->getVertexOrder(Vertices::ORDER_DEGREE)); - } - - /** - * get maximum degree of vertices - * - * @return int - * @throws Exception if graph is empty or directed - * @uses Vertices::getVertexOrder() - * @uses self::getDegreeVertex() - */ - public function getDegreeMax() - { - return $this->getDegreeVertex($this->graph->getVertices()->getVertexOrder(Vertices::ORDER_DEGREE, true)); - } - - /** - * checks whether this graph is regular, i.e. each vertex has the same indegree/outdegree - * - * @return boolean - * @uses self::getDegree() - */ - public function isRegular() - { - // an empty graph is considered regular - if ($this->graph->getVertices()->isEmpty()) { - return true; - } - try { - $this->getDegree(); - - return true; - } catch (UnexpectedValueException $ignore) { } - - return false; - } - - /** - * checks whether the indegree of every vertex equals its outdegree - * - * @return boolean - * @uses self::getDegreeInVertex() - * @uses self::getDegreeOutVertex() - */ - public function isBalanced() - { - foreach ($this->graph->getVertices() as $vertex) { - if ($this->getDegreeInVertex($vertex) !== $this->getDegreeOutVertex($vertex)) { - return false; - } - } - - return true; - } - - /** - * checks whether this vertex is a source, i.e. its indegree is zero - * - * @param Vertex $vertex - * @return boolean - * @uses Edge::hasVertexTarget() - * @see self::getDegreeInVertex() - */ - public function isVertexSource(Vertex $vertex) - { - foreach ($vertex->getEdges() as $edge) { - if ($edge->hasVertexTarget($vertex)) { - return false; - } - } - - // reach this point: no edge to this vertex - return true; - } - - /** - * checks whether this vertex is a sink, i.e. its outdegree is zero - * - * @param Vertex $vertex - * @return boolean - * @uses Edge::hasVertexStart() - * @see self::getDegreeOutVertex() - */ - public function isVertexSink(Vertex $vertex) - { - foreach ($vertex->getEdges() as $edge) { - if ($edge->hasVertexStart($vertex)) { - return false; - } - } - - // reach this point: no edge away from this vertex - return true; - } - - /** - * get degree of this vertex (total number of edges) - * - * vertex degree counts the total number of edges attached to this vertex - * regardless of whether they're directed or not. loop edges are counted - * twice as both start and end form a 'line' to the same vertex. - * - * @param Vertex $vertex - * @return int - * @see self::getDegreeInVertex() - * @see self::getDegreeOutVertex() - */ - public function getDegreeVertex(Vertex $vertex) - { - return count($vertex->getEdges()); - } - - /** - * check whether this vertex is isolated (i.e. has no edges attached) - * - * @param Vertex $vertex - * @return boolean - */ - public function isVertexIsolated(Vertex $vertex) - { - return $vertex->getEdges()->isEmpty(); - } - - /** - * get indegree of this vertex (number of edges TO this vertex) - * - * @param Vertex $vertex - * @return int - * @uses Edge::hasVertexTarget() - * @see self::getDegreeVertex() - */ - public function getDegreeInVertex($vertex) - { - $n = 0; - foreach ($vertex->getEdges() as $edge) { - if ($edge->hasVertexTarget($vertex)) { - ++$n; - } - } - - return $n; - } - - /** - * get outdegree of this vertex (number of edges FROM this vertex TO other vertices) - * - * @param Vertex $vertex - * @return int - * @uses Edge::hasVertexStart() - * @see self::getDegreeVertex() - */ - public function getDegreeOutVertex(Vertex $vertex) - { - $n = 0; - foreach ($vertex->getEdges() as $edge) { - if ($edge->hasVertexStart($vertex)) { - ++$n; - } - } - - return $n; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/DetectNegativeCycle.php b/lib/Fhaculty/Graph/Algorithm/DetectNegativeCycle.php deleted file mode 100644 index 53f37b84..00000000 --- a/lib/Fhaculty/Graph/Algorithm/DetectNegativeCycle.php +++ /dev/null @@ -1,82 +0,0 @@ -getCycleNegative(); - - // cycle was found => okay - return true; - // no cycle found - } catch (UnderflowException $ignore) {} - - return false; - } - - /** - * Searches all vertices for the first negative cycle - * - * @return Walk - * @throws UnderflowException if there's no negative cycle - * @uses AlgorithmSpMooreBellmanFord::getVertices() - */ - public function getCycleNegative() - { - // remember vertices already visited, as they can not lead to a new cycle - $verticesVisited = array(); - // check for all vertices - foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) { - // skip vertices already visited - if (!isset($verticesVisited[$vid])) { - // start MBF algorithm on current vertex - $alg = new SpMooreBellmanFord($vertex); - - try { - // try to get all connected vertices (or throw new cycle) - foreach ($alg->getVertices()->getIds() as $vid) { - // getting connected vertices succeeded, so skip over all of them - $verticesVisited[$vid] = true; - // no cycle found, check next vertex... - } - // yey, negative cycle encountered => return - } catch (NegativeCycleException $e) { - return $e->getCycle(); - } - } - // no more vertices to check => abort - } - throw new UnderflowException('No negative cycle found'); - } - - /** - * create new graph clone with only vertices and edges in negative cycle - * - * @return Graph - * @throws Exception if there's no negative cycle - * @uses AlgorithmDetectNegativeCycle::getCycleNegative() - * @uses Walk::createGraph() - */ - public function createGraph() - { - return $this->getCycleNegative()->createGraph(); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Directed.php b/lib/Fhaculty/Graph/Algorithm/Directed.php deleted file mode 100644 index 03e136c7..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Directed.php +++ /dev/null @@ -1,66 +0,0 @@ -set->getEdges() as $edge) { - if ($edge instanceof EdgeDirected) { - return true; - } - } - - return false; - } - - /** - * checks whether the graph has any undirected edges - * - * This method is intentionally not named "isUndirected()", - * because that might be misleading in regards to empty and/or mixed graphs. - * - * @return boolean - */ - public function hasUndirected() - { - foreach ($this->set->getEdges() as $edge) { - if ($edge instanceof EdgeUndirected) { - return true; - } - } - - return false; - } - - /** - * checks whether this is a mixed graph (contains both directed and undirected edges) - * - * @return boolean - * @uses self::hasDirected() - * @uses self::hasUndirected() - */ - public function isMixed() - { - return ($this->hasDirected() && $this->hasUndirected()); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Eulerian.php b/lib/Fhaculty/Graph/Algorithm/Eulerian.php deleted file mode 100644 index 1dbd7c67..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Eulerian.php +++ /dev/null @@ -1,38 +0,0 @@ -graph); - if ($components->isSingle()) { - $alg = new Degree($this->graph); - - foreach ($this->graph->getVertices() as $vertex) { - // uneven degree => fail - if ($alg->getDegreeVertex($vertex) & 1) { - return false; - } - } - - return true; - } - - return false; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Flow.php b/lib/Fhaculty/Graph/Algorithm/Flow.php deleted file mode 100644 index 7130019b..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Flow.php +++ /dev/null @@ -1,118 +0,0 @@ -set->getEdges() as $edge) { - if ($edge->getFlow() !== NULL) { - return true; - } - } - - return false; - } - - /** - * Calculates the flow for this Vertex: sum(outflow) - sum(inflow) - * - * Usually, vertices should have a resulting flow of 0: The sum of flows - * entering a vertex must equal the sum of flows leaving a vertex. If the - * resulting flow is < 0, this vertex is considered a sink (i.e. there's - * more flow into this vertex). If the resulting flow is > 0, this vertex - * is considered a "source" (i.e. there's more flow leaving this vertex). - * - * @param Vertex $vertex - * @return float - * @throws UnexpectedValueException if they are undirected edges - * @see Vertex::getBalance() - * @uses Vertex::getEdges() - * @uses Edge::getFlow() - */ - public function getFlowVertex(Vertex $vertex) - { - $sumOfFlow = 0; - - foreach ($vertex->getEdges() as $edge) { - if (!($edge instanceof EdgeDirected)) { - throw new UnexpectedValueException("TODO: undirected edges not suported yet"); - } - - // edge is an outgoing edge of this vertex - if ($edge->hasVertexStart($vertex)) { - // flowing out (flow is "pointing away") - $sumOfFlow += $edge->getFlow(); - // this is an ingoing edge - } else { - // flowing in - $sumOfFlow -= $edge->getFlow(); - } - } - - return $sumOfFlow; - } - - public function getBalance() - { - $balance = 0; - // Sum for all vertices of value - foreach ($this->set->getVertices() as $vertex) { - $balance += $vertex->getBalance(); - } - - return $balance; - } - - /** - * check if the current flow is balanced (aka "balanced flow" or "b-flow") - * - * a flow is considered balanced if each edge's current flow does not exceed its - * maximum capacity (which is always guaranteed due to the implementation - * of Edge::setFlow()) and each vertices' flow (i.e. outflow-inflow) equals - * its balance. - * - * checking whether the FLOW is balanced is not to be confused with checking - * whether the GRAPH is balanced (see Graph::isBalanced() instead) - * - * @return boolean - * @see Algorithm\Degree::isBalanced() if you merely want to check indegree=outdegree - * @uses self::getFlowVertex() - * @uses Vertex::getBalance() - */ - public function isBalancedFlow() - { - // no need to check for each edge: flow <= capacity (setters already check that) - // check for each vertex: outflow-inflow = balance - foreach ($this->set->getVertices() as $vertex) { - if ($this->getFlowVertex($vertex) !== $vertex->getBalance()) { - return false; - } - } - - return true; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Groups.php b/lib/Fhaculty/Graph/Algorithm/Groups.php deleted file mode 100644 index d8f55af1..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Groups.php +++ /dev/null @@ -1,87 +0,0 @@ -getGroups()); - } - - /** - * checks whether the input graph's vertex groups are a valid bipartition - * - * @return boolean - * @see AlgorithmBipartit() if you do NOT want to take vertex groups into consideration - * @uses AlgorithmGroups::getNumberOfGroups() - * @uses Vertex::getGroup() - */ - public function isBipartit() - { - // graph has to contain exactly 2 groups - if ($this->getNumberOfGroups() !== 2) { - return false; - } - - // for each vertex - foreach ($this->graph->getVertices() as $vertex) { - // get current group - $group = $vertex->getGroup(); - // for every neighbor vertex - foreach ($vertex->getVerticesEdge() as $vertexNeighbor) { - // vertex group must be other group - if ($vertexNeighbor->getGroup() === $group) { - return false; - } - } - } - - return true; - } - - /** - * get vector of all group numbers - * - * @return int[] - * @uses Vertex::getGroup() - */ - public function getGroups() - { - $groups = array(); - foreach ($this->graph->getVertices() as $vertex) { - $groups[$vertex->getGroup()] = true; - } - - return array_keys($groups); - } - - /** - * get set of all Vertices in the given group - * - * @param int $group - * @return Vertices - * @uses Vertex::getGroup() - */ - public function getVerticesGroup($group) - { - $vertices = array(); - foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) { - if ($vertex->getGroup() === $group) { - $vertices[$vid] = $vertex; - } - } - - return new Vertices($vertices); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Loop.php b/lib/Fhaculty/Graph/Algorithm/Loop.php deleted file mode 100644 index d52bbdd4..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Loop.php +++ /dev/null @@ -1,52 +0,0 @@ -set->getEdges() as $edge) { - if ($edge->isLoop()) { - return true; - } - } - - return false; - } - - /** - * checks whether this vertex has a loop (edge to itself) - * - * @return boolean - * @uses Edge::isLoop() - */ - public function hasLoopVertex(Vertex $vertex) - { - foreach ($vertex->getEdges() as $edge) { - if ($edge->isLoop()) { - return true; - } - } - - return false; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarp.php b/lib/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarp.php deleted file mode 100644 index 00173e6b..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarp.php +++ /dev/null @@ -1,129 +0,0 @@ -getGraph() !== $destinationVertex->getGraph()) { - throw new InvalidArgumentException('Start and target vertex have to be in the same graph instance'); - } - $this->startVertex = $startVertex; - $this->destinationVertex = $destinationVertex; - } - - /** - * Returns max flow graph - * - * @return Graph - */ - public function createGraph() - { - $graphResult = $this->startVertex->getGraph()->createGraphClone(); - - // initialize null flow and check edges - foreach ($graphResult->getEdges() as $edge) { - if (!($edge instanceof EdgeDirected)) { - throw new UnexpectedValueException('Undirected edges not supported for edmonds karp'); - } - $edge->setFlow(0); - } - - $idA = $this->startVertex->getId(); - $idB = $this->destinationVertex->getId(); - - do { - // Generate new residual graph and repeat - $residualAlgorithm = new ResidualGraph($graphResult); - $graphResidual = $residualAlgorithm->createGraph(); - - // 1. Search _shortest_ (number of hops and cheapest) path from s -> t - $alg = new BreadthFirst($graphResidual->getVertex($idA)); - try { - $pathFlow = $alg->getWalkTo($graphResidual->getVertex($idB)); - } catch (OutOfBoundsException $e) { - $pathFlow = NULL; - } - - // If path exists add the new flow to graph - if ($pathFlow) { - // 2. get max flow from path - $maxFlowValue = $pathFlow->getEdges()->getEdgeOrder(Edges::ORDER_CAPACITY)->getCapacity(); - - // 3. add flow to path - foreach ($pathFlow->getEdges() as $edge) { - // try to look for forward edge to increase flow - try { - $originalEdge = $graphResult->getEdgeClone($edge); - $originalEdge->setFlow($originalEdge->getFlow() + $maxFlowValue); - // forward edge not found, look for back edge to decrease flow - } catch (UnderflowException $e) { - $originalEdge = $graphResult->getEdgeCloneInverted($edge); - $originalEdge->setFlow($originalEdge->getFlow() - $maxFlowValue); - } - } - } - - // repeat while we still finds paths with residual capacity to add flow to - } while ($pathFlow); - - return $graphResult; - } - - /** - * Returns max flow value - * - * @return double - */ - public function getFlowMax() - { - $resultGraph = $this->createGraph(); - - $start = $resultGraph->getVertex($this->startVertex->getId()); - $maxFlow = 0; - foreach ($start->getEdgesOut() as $edge) { - $maxFlow = $maxFlow + $edge->getFlow(); - } - - return $maxFlow; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Base.php b/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Base.php deleted file mode 100644 index b728f468..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Base.php +++ /dev/null @@ -1,42 +0,0 @@ -getEdges()); - } - - /** - * create new resulting graph with only edges from maximum matching - * - * @return Graph - * @uses Base::getEdges() - * @uses Graph::createGraphCloneEdges() - */ - public function createGraph() - { - return $this->graph->createGraphCloneEdges($this->getEdges()); - } - - /** - * create new resulting graph with minimum-cost flow on edges - * - * @return Edges - */ - abstract public function getEdges(); -} diff --git a/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Flow.php b/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Flow.php deleted file mode 100644 index d493ecf9..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MaximumMatching/Flow.php +++ /dev/null @@ -1,87 +0,0 @@ -graph); - if ($alg->hasDirected()) { - throw new UnexpectedValueException('Input graph contains directed edges'); - } - - $alg = new Groups($this->graph); - if (!$alg->isBipartit()) { - throw new UnexpectedValueException('Input graph does not have bipartit groups assigned to each vertex. Consider Using "AlgorithmBipartit::createGraph()" first'); - } - - // create temporary flow graph with supersource and supersink - $graphFlow = $this->graph->createGraphCloneEdgeless(); - - $superSource = $graphFlow->createVertex(); - $superSink = $graphFlow->createVertex(); - - $groups = $alg->getGroups(); - $groupA = $groups[0]; - $groupB = $groups[1]; - - // connect supersource s* to set A and supersink t* to set B - foreach ($graphFlow->getVertices() as $vertex) { - // we want to skip over supersource & supersink as they do not have a partition assigned - if ($vertex === $superSource || $vertex === $superSink) continue; - - $group = $vertex->getGroup(); - - // source - if ($group === $groupA) { - $superSource->createEdgeTo($vertex)->setCapacity(1)->setFlow(0); - - // temporarily create edges from A->B for flow graph - $originalVertex = $this->graph->getVertex($vertex->getId()); - foreach ($originalVertex->getVerticesEdgeTo() as $vertexTarget) { - $vertex->createEdgeTo($graphFlow->getVertex($vertexTarget->getId()))->setCapacity(1)->setFlow(0); - } - // sink - } elseif ($group === $groupB) { - $vertex->createEdgeTo($superSink)->setCapacity(1)->setFlow(0); - } else { - // @codeCoverageIgnoreStart - throw new LogicException('Should not happen. Unknown set: ' + $belongingSet); - // @codeCoverageIgnoreEnd - } - } - - // visualize($resultGraph); - - // calculate (s*, t*)-flow - $algMaxFlow = new MaxFlowEdmondsKarp($superSource, $superSink); - $resultGraph = $algMaxFlow->createGraph(); - - // destroy temporary supersource and supersink again - $resultGraph->getVertex($superSink->getId())->destroy(); - $resultGraph->getVertex($superSource->getId())->destroy(); - - $returnEdges = array(); - foreach ($resultGraph->getEdges() as $edge) { - // only keep matched edges - if ($edge->getFlow() > 0) { - $originalEdge = $this->graph->getEdgeClone($edge); - $returnEdges []= $originalEdge; - } - } - - return new Edges($returnEdges); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/Base.php b/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/Base.php deleted file mode 100644 index b8b41c8a..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/Base.php +++ /dev/null @@ -1,83 +0,0 @@ -graph); - $balance = $alg->getBalance(); - - $tolerance = 0.000001; - if ($balance >= $tolerance || $balance <= -$tolerance) { - throw new UnexpectedValueException('The given graph is not balanced value is: ' . $balance); - } - - return $this; - } - - /** - * helper used to add $newFlow to original edges of $clonedEdges in graph $resultGraph - * - * @param Graph $resultGraph graph to look for original edges - * @param Edges $clonedEdges set of cloned edges to be modified - * @param number $newFlow flow to add - * @uses Graph::getEdgeClone() - * @uses Graph::getEdgeCloneInverted() - * @uses Edge::getFlow() - * @uses Edge::setFlow() - */ - protected function addFlow(Graph $resultGraph, Edges $clonedEdges, $newFlow) - { - foreach ($clonedEdges as $clonedEdge) { - try { - // get edge from clone - $edge = $resultGraph->getEdgeClone($clonedEdge); - // add flow - $edge->setFlow($edge->getFlow() + $newFlow); - // if the edge doesn't exist => use the residual edge - } catch (UnderflowException $ignore) { - $edge = $resultGraph->getEdgeCloneInverted($clonedEdge); - // remove flow - $edge->setFlow($edge->getFlow() - $newFlow); - } - } - } - - /** - * calculate total weight along minimum-cost flow - * - * @return float - * @uses self::createGraph() - * @uses AlgorithmWeight::getWeightFlow() - */ - public function getWeightFlow() - { - $alg = new AlgorithmWeight($this->createGraph()); - return $alg->getWeightFlow(); - } - - /** - * create new resulting graph with minimum-cost flow on edges - * - * @throws Exception if the graph has not enough capacity for the minimum-cost flow - * @return Graph - */ - abstract public function createGraph(); -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCanceling.php b/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCanceling.php deleted file mode 100644 index 5ce0d29f..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCanceling.php +++ /dev/null @@ -1,79 +0,0 @@ -checkBalance(); - - // create resulting graph with supersource and supersink - $resultGraph = $this->graph->createGraphClone(); - - $superSource = $resultGraph->createVertex(); - $superSink = $resultGraph->createVertex(); - - $sumBalance = 0; - - // connect supersource s* and supersink t* with all "normal" sources and sinks - foreach ($resultGraph->getVertices() as $vertex) { - $balance = $vertex->getBalance(); - - if ($balance > 0) { - // positive balance => source capacity - $superSource->createEdgeTo($vertex)->setCapacity($balance); - - $sumBalance += $balance; - } elseif ($balance < 0) { - // negative balance => sink capacity (positive) - $vertex->createEdgeTo($superSink)->setCapacity(-$balance); - } - } - - // calculate (s*, t*)-flow - $algMaxFlow = new MaxFlowEdmondsKarp($superSource, $superSink); - $flowMax = $algMaxFlow->getFlowMax(); - - if ($flowMax !== $sumBalance) { - throw new UnexpectedValueException('Network does not support required flow of ' . $sumBalance . ' (maximum possible flow limited to ' . $flowMax . ')'); - } - - $resultGraph = $algMaxFlow->createGraph(); - - while (true) { - // create residual graph - $algRG = new ResidualGraph($resultGraph); - $residualGraph = $algRG->createGraph(); - - // get negative cycle - $alg = new DetectNegativeCycle($residualGraph); - try { - $clonedEdges = $alg->getCycleNegative()->getEdges(); - } catch (UnderflowException $ignore) { - // no negative cycle found => end algorithm - break; - } - - // calculate maximal possible flow = minimum capacity remaining for all edges - $newFlow = $clonedEdges->getEdgeOrder(Edges::ORDER_CAPACITY_REMAINING)->getCapacityRemaining(); - - // set flow on original graph - $this->addFlow($resultGraph, $clonedEdges, $newFlow); - } - - // destroy temporary supersource and supersink again - $resultGraph->getVertex($superSink->getId())->destroy(); - $resultGraph->getVertex($superSource->getId())->destroy(); - - return $resultGraph; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPath.php b/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPath.php deleted file mode 100644 index 3a50c73a..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPath.php +++ /dev/null @@ -1,161 +0,0 @@ -checkBalance(); - $resultGraph = $this->graph->createGraphClone(); - - // initial balance to 0 - $vertices = $resultGraph->getVertices(); - foreach ($vertices as $vertex) { - $vertex->setBalance(0); - } - - // initial flow of edges - $edges = $resultGraph->getEdges(); - foreach ($edges as $edge) { - if (!($edge instanceof EdgeDirected)) { - throw new UnexpectedValueException('Undirected edges are not supported for SuccessiveShortestPath'); - } - - // 0 if weight of edge is positive - $flow = 0; - - // maximal flow if weight of edge is negative - if ($edge->getWeight() < 0) { - $flow = $edge->getCapacity(); - - $startVertex = $edge->getVertexStart(); - $endVertex = $edge->getVertexEnd(); - - // add balance to start- and end-vertex - $this->addBalance($startVertex, $flow); - $this->addBalance($endVertex, - $flow); - } - - $edge->setFlow($flow); - } - - // return or Exception inside this while - while (true) { - // create residual graph - $algRG = new ResidualGraph($resultGraph); - $residualGraph = $algRG->createGraph(); - - // search for a source - try { - $sourceVertex = $this->getVertexSource($residualGraph); - } catch (UnderflowException $ignore) { - // no source is found => minimum-cost flow is found - break; - } - - // search for reachable target sink from this source - try { - $targetVertex = $this->getVertexSink($sourceVertex); - } catch (UnderflowException $e) { - // no target found => network does not have enough capacity - throw new UnexpectedValueException('The graph has not enough capacity for the minimum-cost flow', 0, $e); - } - - // calculate shortest path between source- and target-vertex - $algSP = new SpMooreBellmanFord($sourceVertex); - $edgesOnFlow = $algSP->getEdgesTo($targetVertex); - - // calculate the maximal possible flow - // new flow is the maximal possible flow for this path - $newflow = $this->graph->getVertex($sourceVertex->getId())->getBalance() - $sourceVertex->getBalance(); - $targetFlow = - ($this->graph->getVertex($targetVertex->getId())->getBalance() - $targetVertex->getBalance()); - - // get minimum of source and target - if ($targetFlow < $newflow) { - $newflow = $targetFlow; - } - - // get minimum of capacity remaining on path - $minCapacity = $edgesOnFlow->getEdgeOrder(Edges::ORDER_CAPACITY_REMAINING)->getCapacityRemaining(); - if ($minCapacity < $newflow) { - $newflow = $minCapacity; - } - - // add the new flow to the path - $this->addFlow($resultGraph, $edgesOnFlow, $newflow); - - // add balance to source and remove for the target sink - $oriSourceVertex = $resultGraph->getVertex($sourceVertex->getId()); - $oriTargetVertex = $resultGraph->getVertex($targetVertex->getId()); - - $this->addBalance($oriSourceVertex, $newflow); - $this->addBalance($oriTargetVertex, - $newflow); - } - - return $resultGraph; - } - - /** - * - * - * @param Graph $graph - * @throws Exception if there is no left source vertex - * - * @return Vertex a source vertex in the given graph - */ - private function getVertexSource(Graph $graph) - { - foreach ($graph->getVertices()->getMap() as $vid => $vertex) { - if ($this->graph->getVertex($vid)->getBalance() - $vertex->getBalance() > 0) { - return $vertex; - } - } - throw new UnderflowException('No source vertex found in graph'); - } - - /** - * - * - * @param Vertex $source - * @throws Exception if there is no reachable sink vertex - * - * @return Vertex a sink-vertex that is reachable from the source - * @uses BreadthFirst::getVertices() - */ - private function getVertexSink(Vertex $source) - { - // search for reachable Vertices - $algBFS = new SearchBreadthFirst($source); - - foreach ($algBFS->getVertices()->getMap() as $vid => $vertex) { - if ($this->graph->getVertex($vid)->getBalance() - $vertex->getBalance() < 0) { - return $vertex; - } - } - throw new UnderflowException('No sink vertex connected to given source vertex found'); - } - - private function addBalance(Vertex $vertex, $balance) - { - $vertex->setBalance($vertex->getBalance() + $balance); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Base.php b/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Base.php deleted file mode 100644 index 3448a7e9..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Base.php +++ /dev/null @@ -1,91 +0,0 @@ -getGraph()->createGraphCloneEdges($this->getEdges()); - } - - /** - * get all edges on minimum spanning tree - * - * @return Edges - */ - abstract public function getEdges(); - - /** - * return reference to current Graph - * - * @return Graph - */ - abstract protected function getGraph(); - - /** - * get total weight of minimum spanning tree - * - * @return float - */ - public function getWeight() - { - return $this->getEdges()->getSumCallback(function (Edge $edge) { - return $edge->getWeight(); - }); - } - - /** - * helper method to add a set of Edges to the given set of sorted edges - * - * @param Edges $edges - * @param SplPriorityQueue $sortedEdges - */ - protected function addEdgesSorted(Edges $edges, SplPriorityQueue $sortedEdges) - { - // For all edges - foreach ($edges as $edge) { - /* @var $edge Edge */ - // ignore loops (a->a) - if (!$edge->isLoop()) { - // Add edges with negative weight because of order in stl - $sortedEdges->insert($edge, -$edge->getWeight()); - } - } - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Kruskal.php b/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Kruskal.php deleted file mode 100644 index 6c29a90e..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Kruskal.php +++ /dev/null @@ -1,134 +0,0 @@ -graph = $inputGraph; - } - - protected function getGraph() - { - return $this->graph; - } - - /** - * - * @return Edges - */ - public function getEdges() - { - // Sortiere Kanten im Graphen - - $sortedEdges = new SplPriorityQueue(); - - // For all edges - $this->addEdgesSorted($this->graph->getEdges(), $sortedEdges); - - $returnEdges = array(); - - // next color to assign - $colorNext = 0; - // array(color1 => array(vid1, vid2, ...), color2=>...) - $colorVertices = array(); - // array(vid1 => color1, vid2 => color1, ...) - $colorOfVertices = array(); - - // Füge billigste Kanten zu neuen Graphen hinzu und verschmelze teilgragen wenn es nötig ist (keine Kreise) - // solange ich mehr als einen Graphen habe mit weniger als n-1 kanten (bei n knoten im original) - foreach ($sortedEdges as $edge) { - /* @var $edge EdgeDirected */ - // Gucke Kante an: - - $vertices = $edge->getVertices()->getIds(); - - $aId = $vertices[0]; - $bId = $vertices[1]; - - $aColor = isset($colorOfVertices[$aId]) ? $colorOfVertices[$aId] : NULL; - $bColor = isset($colorOfVertices[$bId]) ? $colorOfVertices[$bId] : NULL; - - // 1. weder start noch end gehört zu einem graphen - // => neuer Graph mit kanten - if ($aColor === NULL && $bColor === NULL) { - $colorOfVertices[$aId] = $colorNext; - $colorOfVertices[$bId] = $colorNext; - - $colorVertices[$colorNext] = array($aId, $bId); - - ++$colorNext; - - // connect both vertices - $returnEdges []= $edge; - } - // 4. start xor end gehören zu einem graphen - // => erweitere diesesn Graphen - // Only b has color - else if ($aColor === NULL && $bColor !== NULL) { - // paint a in b's color - $colorOfVertices[$aId] = $bColor; - $colorVertices[$bColor][]=$aId; - - $returnEdges []= $edge; - // Only a has color - } elseif ($aColor !== NULL && $bColor === NULL) { - // paint b in a's color - $colorOfVertices[$bId] = $aColor; - $colorVertices[$aColor][]=$bId; - - $returnEdges []= $edge; - } - // 3. start und end gehören zu unterschiedlichen graphen - // => vereinigung - // Different color - else if ($aColor !== $bColor) { - $betterColor = $aColor; - $worseColor = $bColor; - - // more vertices with color a => paint all in b in a's color - if (count($colorVertices[$bColor]) > count($colorVertices[$aColor])) { - $betterColor = $bColor; - $worseColor = $aColor; - } - - // search all vertices with color b - foreach ($colorVertices[$worseColor] as $vid) { - $colorOfVertices[$vid] = $betterColor; - // repaint in a's color - $colorVertices[$betterColor][]=$vid; - } - // delete old color - unset($colorVertices[$worseColor]); - - $returnEdges []= $edge; - } - // 2. start und end gehören zum gleichen graphen => zirkel - // => nichts machen - } - - // definition of spanning tree: number of edges = number of vertices - 1 - // above algorithm does not check isolated edges or may otherwise return multiple connected components => force check - if (count($returnEdges) !== (count($this->graph->getVertices()) - 1)) { - throw new UnexpectedValueException('Graph is not connected'); - } - - return new Edges($returnEdges); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Prim.php b/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Prim.php deleted file mode 100644 index 51518462..00000000 --- a/lib/Fhaculty/Graph/Algorithm/MinimumSpanningTree/Prim.php +++ /dev/null @@ -1,81 +0,0 @@ -startVertex = $startVertex; - } - - /** - * - * @return Edges - */ - public function getEdges() - { - // Initialize algorithm - $edgeQueue = new SplPriorityQueue(); - $vertexCurrent = $this->startVertex; - - $markInserted = array(); - $returnEdges = array(); - - // iterate n-1 times (per definition, resulting MST MUST have n-1 edges) - for ($i = 0, $n = count($this->startVertex->getGraph()->getVertices()) - 1; $i < $n; ++$i) { - $markInserted[$vertexCurrent->getId()] = true; - - // get unvisited vertex of the edge and add edges from new vertex - // Add all edges from $currentVertex to priority queue - $this->addEdgesSorted($vertexCurrent->getEdges(), $edgeQueue); - - do { - try { - // Get next cheapest edge - $cheapestEdge = $edgeQueue->extract(); - /* @var $cheapestEdge EdgeDirected */ - } catch (Exception $e) { - throw new UnexpectedValueException('Graph has more than one component', 0, $e); - } - - // Check if edge is between unmarked and marked edge - - $vertices = $cheapestEdge->getVertices(); - $vertexA = $vertices->getVertexFirst(); - $vertexB = $vertices->getVertexLast(); - - // Edge is between marked and unmared vertex - } while (!(isset($markInserted[$vertexA->getId()]) XOR isset($markInserted[$vertexB->getId()]))); - - // Cheapest Edge found, add edge to returnGraph - $returnEdges []= $cheapestEdge; - - // set current vertex for next iteration in order to add its edges to queue - if (isset($markInserted[$vertexA->getId()])) { - $vertexCurrent = $vertexB; - } else { - $vertexCurrent = $vertexA; - } - } - - return new Edges($returnEdges); - } - - protected function getGraph() - { - return $this->startVertex->getGraph(); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Parallel.php b/lib/Fhaculty/Graph/Algorithm/Parallel.php deleted file mode 100644 index 390cace4..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Parallel.php +++ /dev/null @@ -1,84 +0,0 @@ -graph->getEdges() as $edge) { - if ($this->hasEdgeParallelEdge($edge)) { - return true; - } - } - - return false; - } - - - /** - * checks whether this edge has any parallel edges - * - * @return boolean - * @uses Edge::getEdgesParallel() - */ - public function hasEdgeParallelEdge(Edge $edge) - { - return !$this->getEdgesParallelEdge($edge)->isEmpty(); - } - - /** - * get set of all Edges parallel to this edge (excluding self) - * - * @param Edge $edge - * @return Edges - * @throws LogicException - */ - public function getEdgesParallelEdge(Edge $edge) - { - if ($edge instanceof DirectedEdge) { - // get all edges between this edge's endpoints - $edges = $edge->getVertexStart()->getEdgesTo($edge->getVertexEnd())->getVector(); - } else { - // edge points into both directions (undirected/bidirectional edge) - // also get all edges in other direction - $ends = $edge->getVertices(); - $edges = $ends->getVertexFirst()->getEdges()->getEdgesIntersection($ends->getVertexLast()->getEdges())->getVector(); - } - - $pos = array_search($edge, $edges, true); - - if ($pos === false) { - // @codeCoverageIgnoreStart - throw new LogicException('Internal error: Current edge not found'); - // @codeCoverageIgnoreEnd - } - - // exclude current edge from parallel edges - unset($edges[$pos]); - - return new Edges(array_values($edges)); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Property/GraphProperty.php b/lib/Fhaculty/Graph/Algorithm/Property/GraphProperty.php deleted file mode 100644 index 90c21575..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Property/GraphProperty.php +++ /dev/null @@ -1,51 +0,0 @@ -graph->getEdges()->isEmpty(); - } - - /** - * checks whether this graph is a null graph (no vertex - and thus no edges) - * - * Each Edge is incident to two Vertices, or in case of an loop Edge, - * incident to the same Vertex twice. As such an Edge can not exist when - * no Vertices exist. So if we check we have no Vertices, we can also be - * sure that no Edges exist either. - * - * @return boolean - */ - public function isNull() - { - return $this->graph->getVertices()->isEmpty(); - } - - /** - * checks whether this graph is trivial (one vertex and no edges) - * - * @return boolean - */ - public function isTrivial() - { - return ($this->graph->getEdges()->isEmpty() && count($this->graph->getVertices()) === 1); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Property/WalkProperty.php b/lib/Fhaculty/Graph/Algorithm/Property/WalkProperty.php deleted file mode 100644 index 427ecf10..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Property/WalkProperty.php +++ /dev/null @@ -1,381 +0,0 @@ -walk = $walk; - } - - /** - * checks whether walk is a cycle (i.e. source vertex = target vertex) - * - * A cycle is also known as a closed path, a walk that is NOT a cycle is - * also known as an open path. - * - * A walk with no edges is not considered a cycle. The shortest possible - * cycle is a single loop edge: - * - * 1--\ - * ^ | - * \--/ - * - * The following Walk is also considered a valid cycle: - * - * /->3--\ - * | | - * 1 -> 2 -\ | - * ^ ^ | | - * | \--/ | - * | | - * \----------/ - * - * @return bool - * @link http://en.wikipedia.org/wiki/Cycle_%28graph_theory%29 - * @see self::isCircuit() - * @see self::isLoop() - */ - public function isCycle() - { - $vertices = $this->walk->getVertices(); - return ($vertices->getVertexFirst() === $vertices->getVertexLast() && !$this->walk->getEdges()->isEmpty()); - } - - /** - * checks whether this walk is a circuit (i.e. a cycle with no duplicate edges) - * - * A circuit is also known as a closed (=cycle) trail (=path), that has at - * least one edge. - * - * The following Walk is considered both a valid cycle and a valid circuit: - * - * 1 -> 2 -> 3 -\ - * ^ | - * | | - * \------------/ - * - * The following Walk is also considered both a valid cycle and a valid circuit: - * - * /->3--\ - * | | - * 1 -> 2 -\ | - * ^ ^ | | - * | \--/ | - * | | - * \----------/ - * - * The later circuit walk can be expressed by its Vertex IDs as - * "1, 2, 2, 3, 1". If however, the inner loop would be "walked along" - * several times, the resulting walk would be expressed as - * "1, 2, 2, 2, 3, 1", which would still be a valid cycle, but NOT a valid - * circuit anymore. - * - * @return boolean - * @link http://www.proofwiki.org/wiki/Definition:Circuit - * @uses self::isCycle() - * @uses self::isPath() - */ - public function isCircuit() - { - return ($this->isCycle() && $this->isPath()); - } - - /** - * checks whether walk is a path (i.e. does not contain any duplicate edges) - * - * A path Walk is also known as a trail. - * - * @return bool - * @uses self::hasArrayDuplicates() - * @link http://www.proofwiki.org/wiki/Definition:Trail - */ - public function isPath() - { - return !$this->hasArrayDuplicates($this->walk->getEdges()->getVector()); - } - - /** - * checks whether walk contains a cycle (i.e. contains a duplicate vertex) - * - * A walk that CONTAINS a cycle does not neccessarily have to BE a cycle. - * Conversely, a Walk that *is* a cycle, automatically always *contains* a - * cycle. - * - * The following Walk is NOT a cycle, but it *contains* a valid cycle: - * - * /->4 - * | - * 1 -> 2 -> 3 -\ - * ^ | - * \-------/ - * - * @return bool - * @uses self::hasArrayDuplicates() - * @see self::isCycle() - */ - public function hasCycle() - { - return $this->hasArrayDuplicates($this->walk->getVertices()->getVector()); - } - - /** - * checks whether this walk IS a loop (single edge connecting vertex A with vertex A again) - * - * A loop is the simplest possible cycle. As such, each loop is also a - * cycle. Accordingly, every Walk that *is* a loop, automatically also *is* - * a cycle and automatically *contains* a loop and automatically *contains* - * a cycle. - * - * The following Walk represents a simple (directed) loop: - * - * 1--\ - * ^ | - * \--/ - * - * @return boolean - * @uses self::isCycle() - * @see self::hasLoop() - */ - public function isLoop() - { - return (count($this->walk->getEdges()) === 1 && $this->isCycle()); - } - - /** - * checks whether this walk HAS a loop (single edge connecting vertex A with vertex A again) - * - * The following Walk is NOT a valid loop, but it contains a valid loop: - * - * /->3 - * | - * 1 -> 2 -\ - * ^ | - * \--/ - * - * @return boolean - * @uses AlgorithmLoop::hasLoop() - * @see self::isLoop() - */ - public function hasLoop() - { - $alg = new AlgorithmLoop($this->walk); - - return $alg->hasLoop(); - } - - /** - * checks whether this walk is a digon (a pair of parallel edges in a multigraph or a pair of antiparallel edges in a digraph) - * - * A digon is a cycle connecting exactly two distinct vertices with exactly - * two distinct edges. - * - * The following Graph represents a digon in an undirected Graph: - * - * /--\ - * 1 2 - * \--/ - * - * The following Graph represents a digon as a set of antiparallel directed - * Edges in a directed Graph: - * - * 1 -> 2 - * ^ | - * | | - * \----/ - * - * @return boolean - * @uses self::hasArrayDuplicates() - * @uses self::isCycle() - */ - public function isDigon() - { - // exactly 2 edges - return (count($this->walk->getEdges()) === 2 && - // no duplicate edges - !$this->hasArrayDuplicates($this->walk->getEdges()->getVector()) && - // exactly two distinct vertices - count($this->walk->getVertices()->getVerticesDistinct()) === 2 && - // this is actually a cycle - $this->isCycle()); - } - - /** - * checks whether this walk is a triangle (a simple cycle with exactly three distinct vertices) - * - * The following Graph is a valid directed triangle: - * - * 1->2->3 - * ^ | - * \-----/ - * - * @return boolean - * @uses self::isCycle() - */ - public function isTriangle() - { - // exactly 3 (implicitly distinct) edges - return (count($this->walk->getEdges()) === 3 && - // exactly three distinct vertices - count($this->walk->getVertices()->getVerticesDistinct()) === 3 && - // this is actually a cycle - $this->isCycle()); - } - - /** - * check whether this walk is simple - * - * contains no duplicate/repeated vertices (and thus no duplicate edges either) - * other than the starting and ending vertices of cycles. - * - * A simple Walk is also known as a chain. - * - * The term "simple walk" is somewhat related to a walk with no cycles. If - * a Walk has a cycle, it is not simple - with one single exception: a Walk - * that IS a cycle automatically also contains a cycle, but if it contains - * no "further" additional cycles, it is considered a simple cycle. - * - * The following Graph represents a (very) simple Walk: - * - * 1 -- 2 - * - * The following Graph IS a cycle and is simple: - * - * 1 -> 2 - * ^ | - * \----/ - * - * The following Graph contains a cycle and is NOT simple: - * - * /->4 - * | - * 1 -> 2 -> 3 -\ - * ^ | - * \-------/ - * - * The following Graph IS a cycle and thus automatically contains a cycle. - * Due to the additional "inner" cycle (loop at vertex 2), it is NOT simple: - * - * /->3--\ - * | | - * 1 -> 2 -\ | - * ^ ^ | | - * | \--/ | - * | | - * \----------/ - * - * @return boolean - * @uses self::isCycle() - * @uses self::hasArrayDuplicates() - * @see self::hasCycle() - */ - public function isSimple() - { - $vertices = $this->walk->getVertices()->getVector(); - // ignore starting vertex for cycles as it's always the same as ending vertex - if ($this->isCycle()) { - unset($vertices[0]); - } - - return !$this->hasArrayDuplicates($vertices); - } - - /** - * checks whether walk is hamiltonian (i.e. walk over ALL VERTICES of the graph) - * - * A hamiltonian Walk is also known as a spanning walk. - * - * @return boolean - * @see self::isEulerian() if you want to check for all EDGES instead of VERTICES - * @uses self::isArrayContentsEqual() - * @link http://en.wikipedia.org/wiki/Hamiltonian_path - */ - public function isHamiltonian() - { - $vertices = $this->walk->getVertices()->getVector(); - // ignore starting vertex for cycles as it's always the same as ending vertex - if ($this->isCycle()) { - unset($vertices[0]); - } - return $this->isArrayContentsEqual($vertices, $this->walk->getGraph()->getVertices()->getVector()); - } - - /** - * checks whether walk is eulerian (i.e. a walk over ALL EDGES of the graph) - * - * @return boolean - * @see self::isHamiltonian() if you want to check for all VERTICES instead of EDGES - * @uses self::isArrayContentsEqual() - * @link http://en.wikipedia.org/wiki/Eulerian_path - */ - public function isEulerian() - { - return $this->isArrayContentsEqual($this->walk->getEdges()->getVector(), $this->walk->getGraph()->getEdges()->getVector()); - } - - /** - * checks whether ths given array contains duplicate identical entries - * - * @param array $array - * @return bool - */ - private function hasArrayDuplicates($array) - { - $compare = array(); - foreach ($array as $element) { - // duplicate element found - if (in_array($element, $compare, true)) { - return true; - } else { - // add element to temporary array to check for duplicates - $compare [] = $element; - } - } - - return false; - } - - /** - * checks whether the contents of array a equals those of array b (ignore keys and order but otherwise strict check) - * - * @param array $a - * @param array $b - * @return boolean - */ - private function isArrayContentsEqual($a, $b) - { - foreach ($b as $one) { - $pos = array_search($one, $a, true); - if ($pos === false) { - return false; - } else { - unset($a[$pos]); - } - } - - return $a ? false : true; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/ResidualGraph.php b/lib/Fhaculty/Graph/Algorithm/ResidualGraph.php deleted file mode 100644 index 27d65786..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ResidualGraph.php +++ /dev/null @@ -1,109 +0,0 @@ -keepNullCapacity = !!$toggle; - - return $this; - } - - public function setMergeParallelEdges($toggle) - { - $this->mergeParallelEdges = !!$toggle; - - return $this; - } - - /** - * create residual graph - * - * @throws UnexpectedValueException if input graph has undirected edges or flow/capacity is not set - * @return Graph - * @uses Graph::createGraphCloneEdgeless() - * @uses Graph::createEdgeClone() - * @uses Graph::createEdgeCloneInverted() - */ - public function createGraph() - { - $newgraph = $this->graph->createGraphCloneEdgeless(); - - foreach ($this->graph->getEdges() as $edge) { - if (!($edge instanceof EdgeDirected)) { - throw new UnexpectedValueException('Edge is undirected'); - } - - $flow = $edge->getFlow(); - if ($flow === NULL) { - throw new UnexpectedValueException('Flow not set'); - } - - $capacity = $edge->getCapacity(); - if ($capacity === NULL) { - throw new UnexpectedValueException('Capacity not set'); - } - - // capacity is still available, clone remaining capacity into new edge - if ($this->keepNullCapacity || $flow < $capacity) { - $newEdge = $newgraph->createEdgeClone($edge)->setFlow(0)->setCapacity($capacity - $flow); - - if ($this->mergeParallelEdges) { - $this->mergeParallelEdges($newEdge); - } - } - - // flow is set, clone current flow as capacity for back-flow into new inverted edge (opposite direction) - if ($this->keepNullCapacity || $flow > 0) { - $newEdge = $newgraph->createEdgeCloneInverted($edge)->setFlow(0)->setCapacity($flow); - - // if weight is set, use negative weight for back-edges - if ($newEdge->getWeight() !== NULL) { - $newEdge->setWeight(-$newEdge->getWeight()); - } - - if ($this->mergeParallelEdges) { - $this->mergeParallelEdges($newEdge); - } - } - } - - return $newgraph; - } - - /** - * Will merge all edges that are parallel to to given edge - * - * @param Edge $newEdge - */ - private function mergeParallelEdges(Edge $newEdge) - { - $parallelEdges = $newEdge->getEdgesParallel(); - if ($parallelEdges) { - - $mergedCapacity = 0; - - foreach ($parallelEdges as $parallelEdge) { - $mergedCapacity += $parallelEdge->getCapacity(); - } - - $newEdge->setCapacity($newEdge->getCapacity() + $mergedCapacity); - - foreach ($parallelEdges as $parallelEdge) { - $parallelEdge->destroy(); - } - } - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Search/Base.php b/lib/Fhaculty/Graph/Algorithm/Search/Base.php deleted file mode 100644 index a47bf921..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Search/Base.php +++ /dev/null @@ -1,56 +0,0 @@ -direction = $direction; - - return $this; - } - - protected function getVerticesAdjacent(Vertex $vertex) - { - if ($this->direction === self::DIRECTION_FORWARD) { - return $vertex->getVerticesEdgeTo(); - } elseif ($this->direction === self::DIRECTION_REVERSE) { - return $vertex->getVerticesEdgeFrom(); - } elseif ($this->direction === self::DIRECTION_BOTH) { - return $vertex->getVerticesEdge(); - } else { - throw new DomainException('Should not happen. Invalid direction setting'); - } - } - - /** - * get set of all Vertices that can be reached from start vertex - * - * @return Vertices - */ - abstract public function getVertices(); -} diff --git a/lib/Fhaculty/Graph/Algorithm/Search/BreadthFirst.php b/lib/Fhaculty/Graph/Algorithm/Search/BreadthFirst.php deleted file mode 100644 index 52919af7..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Search/BreadthFirst.php +++ /dev/null @@ -1,45 +0,0 @@ -vertex); - // to not add vertices twice in array visited - $mark = array($this->vertex->getId() => true); - // visited vertices - $visited = array(); - - do { - // get first from queue - $t = array_shift($queue); - // save as visited - $visited[$t->getId()]= $t; - - // get next vertices - foreach ($this->getVerticesAdjacent($t)->getMap() as $id => $vertex) { - // if not "touched" before - if (!isset($mark[$id])) { - // add to queue - $queue[] = $vertex; - // and mark - $mark[$id] = true; - } - } - - // untill queue is empty - } while ($queue); - - return new Vertices($visited); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Search/DepthFirst.php b/lib/Fhaculty/Graph/Algorithm/Search/DepthFirst.php deleted file mode 100644 index 6cec963e..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Search/DepthFirst.php +++ /dev/null @@ -1,66 +0,0 @@ -visitedVertices - * - * @param Vertex $vertex - */ - private function recursiveDepthFirstSearch(Vertex $vertex, array & $visitedVertices) - { - // If I didn't visited this vertex before - if (!isset($visitedVertices[$vertex->getId()])) { - // Add Vertex to already visited vertices - $visitedVertices[$vertex->getId()] = $vertex; - - // Get next vertices - $nextVertices = $vertex->getVerticesEdgeTo(); - - foreach ($nextVertices as $nextVertix) { - // recursive call for next vertices - $this->recursiveDepthFirstSearch($nextVertix, $visitedVertices); - } - } - } - - private function iterativeDepthFirstSearch(Vertex $vertex) - { - $visited = array(); - $todo = array($vertex); - while ($vertex = array_shift($todo)) { - if (!isset($visited[$vertex->getId()])) { - $visited[$vertex->getId()] = $vertex; - - foreach (array_reverse($this->getVerticesAdjacent($vertex)->getMap(), true) as $vid => $nextVertex) { - $todo[] = $nextVertex; - } - } - } - - return new Vertices($visited); - } - - /** - * calculates a recursive depth-first search - * - * @return Vertices - */ - public function getVertices() - { - return $this->iterativeDepthFirstSearch($this->vertex); - - $visitedVertices = array(); - $this->recursiveDepthFirstSearch($this->vertex, $visitedVertices); - - return $visitedVertices; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/ShortestPath/Base.php b/lib/Fhaculty/Graph/Algorithm/ShortestPath/Base.php deleted file mode 100644 index d3df57cc..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ShortestPath/Base.php +++ /dev/null @@ -1,287 +0,0 @@ -C->D->B" - * with a distance (total weight) of 6. - * - * In graph theory, it is usually assumed that a path to an unreachable vertex - * has infinite distance. In the above pictured graph, there's no way path - * from A to F, i.e. vertex F is unreachable from vertex A because of the - * directed edge "E <- F" pointing in the opposite direction. This library - * considers this an Exception instead. So if you're asking for the distance - * between A and F, you'll receive an OutOfBoundsException instead. - * - * In graph theory, it is usually assumed that each vertex has a (pseudo-)path - * to itself with a distance of 0. In order to produce reliable, consistent - * results, this library considers this (pseudo-)path to be non-existant, i.e. - * there's NO "magic" path between A and A. So if you're asking for the distance - * between A and A, you'll receive an OutOfBoundsException instead. This allows - * us to check hether there's a real path between A and A (cycle via other - * vertices) as well as working with loop edges. - * - * @link http://en.wikipedia.org/wiki/Shortest_path_problem - * @link http://en.wikipedia.org/wiki/Tree_%28data_structure%29 - * @see ShortestPath\Dijkstra - * @see ShortestPath\MooreBellmanFord which also supports negative Edge weights - * @see ShortestPath\BreadthFirst with does not consider Edge weights, but only the number of hops - */ -abstract class Base extends BaseVertex -{ - /** - * get walk (path) from start vertex to given end vertex - * - * @param Vertex $endVertex - * @return Walk - * @throws OutOfBoundsException if there's no path to the given end vertex - * @uses self::getEdgesTo() - * @uses Walk::factoryFromEdges() - */ - public function getWalkTo(Vertex $endVertex) - { - return Walk::factoryFromEdges($this->getEdgesTo($endVertex), $this->vertex); - } - - /** - * get array of edges (path) from start vertex to given end vertex - * - * @param Vertex $endVertex - * @throws OutOfBoundsException if there's no path to the given end vertex - * @return Edges - * @uses self::getEdges() - * @uses self::getEdgesToInternal() - */ - public function getEdgesTo(Vertex $endVertex) - { - return $this->getEdgesToInternal($endVertex, $this->getEdges()); - } - - /** - * get array of edges (path) from start vertex to given end vertex - * - * @param Vertex $endVertex - * @param Edges|Edge[] $edges set or array of all input edges to operate on - * @throws OutOfBoundsException if there's no path to the given vertex - * @return Edges - * @uses self::getEdges() if no edges were given - */ - protected function getEdgesToInternal(Vertex $endVertex, $edges) - { - $currentVertex = $endVertex; - $path = array(); - do { - $pre = NULL; - // check all edges to search for edge that points TO current vertex - foreach ($edges as $edge) { - try { - // get start point of this edge (fails if current vertex is not its end point) - $pre = $edge->getVertexFromTo($currentVertex); - $path []= $edge; - $currentVertex = $pre; - break; - } catch (InvalidArgumentException $ignore) { - } // ignore: this edge does not point TO current vertex - } - if ($pre === NULL) { - throw new OutOfBoundsException('No edge leading to vertex'); - } - } while ($currentVertex !== $this->vertex); - - return new Edges(array_reverse($path)); - } - - /** - * get sum of weight of given edges - * - * @param Edges $edges - * @return float - * @uses Edge::getWeight() - */ - private function sumEdges(Edges $edges) - { - $sum = 0; - foreach ($edges as $edge) { - $sum += $edge->getWeight(); - } - - return $sum; - } - - /** - * get set of all Vertices the given start vertex has a path to - * - * @return Vertices - * @uses self::getDistanceMap() - */ - public function getVertices() - { - $vertices = array(); - $map = $this->getDistanceMap(); - foreach ($this->vertex->getGraph()->getVertices()->getMap() as $vid => $vertex) { - if (isset($map[$vid])) { - $vertices[$vid] = $vertex; - } - } - - return new Vertices($vertices); - } - - /** - * checks whether there's a path from this start vertex to given end vertex - * - * @param Vertex $endVertex - * @return boolean - * @uses self::getEdgesTo() - */ - public function hasVertex(Vertex $vertex) - { - try { - $this->getEdgesTo($vertex); - } - catch (OutOfBoundsException $e) { - return false; - } - return true; - } - - /** - * get map of vertex IDs to distance - * - * @return float[] - * @uses self::getEdges() - * @uses self::getEdgesToInternal() - * @uses self::sumEdges() - */ - public function getDistanceMap() - { - $edges = $this->getEdges(); - $ret = array(); - foreach ($this->vertex->getGraph()->getVertices()->getMap() as $vid => $vertex) { - try { - $ret[$vid] = $this->sumEdges($this->getEdgesToInternal($vertex, $edges)); - } catch (OutOfBoundsException $ignore) { - } // ignore vertices that can not be reached - } - - return $ret; - } - - /** - * get distance (sum of weights) between start vertex and given end vertex - * - * @param Vertex $endVertex - * @return float - * @throws OutOfBoundsException if there's no path to the given end vertex - * @uses self::getEdgesTo() - * @uses self::sumEdges() - */ - public function getDistance(Vertex $endVertex) - { - return $this->sumEdges($this->getEdgesTo($endVertex)); - } - - /** - * create new resulting graph with only edges on shortest path - * - * The resulting Graph will always represent a tree with the start vertex - * being the root vertex. - * - * For example considering the following input Graph with equal weights on - * each edge: - * - * A----->F - * / \ ^ - * / \ / - * / \ / - * | E - * | \ - * | \ - * B--->C<---D - * - * The resulting shortest path tree Graph will look like this: - * - * A----->F - * / \ - * / \ - * / \ - * | E - * | \ - * | \ - * B--->C D - * - * Or by just arranging the Vertices slightly different: - * - * A - * /|\ - * / | \ - * B E \->F - * / | - * C<-/ D - * - * @return Graph - * @uses self::getEdges() - * @uses Graph::createGraphCloneEdges() - */ - public function createGraph() - { - return $this->vertex->getGraph()->createGraphCloneEdges($this->getEdges()); - } - - /** - * get cheapest edges (lowest weight) for given map of vertex predecessors - * - * @param Vertex[] $predecessor - * @return Edges - * @uses Graph::getVertices() - * @uses Vertex::getEdgesTo() - * @uses Edges::getEdgeOrder() - */ - protected function getEdgesCheapestPredecesor(array $predecessor) - { - $vertices = $this->vertex->getGraph()->getVertices()->getMap(); - - $edges = array(); - foreach ($vertices as $vid => $vertex) { - if (isset($predecessor[$vid])) { - // get predecor - $predecesVertex = $predecessor[$vid]; - - // get cheapest edge - $edges []= $predecesVertex->getEdgesTo($vertex)->getEdgeOrder(Edges::ORDER_WEIGHT); - } - } - - return new Edges($edges); - } - - /** - * get all edges on shortest path for this vertex - * - * @return Edges - */ - abstract public function getEdges(); -} diff --git a/lib/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirst.php b/lib/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirst.php deleted file mode 100644 index c1494088..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirst.php +++ /dev/null @@ -1,132 +0,0 @@ -getEdgesTo($endVertex)); - } - - /** - * get array of edges on the walk for each vertex (vertex ID => array of walk edges) - * - * @return array[] - */ - public function getEdgesMap() - { - $vertexQueue = array(); - $edges = array(); - - // $edges[$this->vertex->getId()] = array(); - - $vertexCurrent = $this->vertex; - $edgesCurrent = array(); - - do { - foreach ($vertexCurrent->getEdgesOut() as $edge) { - $vertexTarget = $edge->getVertexToFrom($vertexCurrent); - $vid = $vertexTarget->getId(); - if (!isset($edges[$vid])) { - $vertexQueue []= $vertexTarget; - $edges[$vid] = array_merge($edgesCurrent, array($edge)); - } - } - - // get next from queue - $vertexCurrent = array_shift($vertexQueue); - if ($vertexCurrent) { - $edgesCurrent = $edges[$vertexCurrent->getId()]; - } - // untill queue is empty - } while ($vertexCurrent); - - return $edges; - } - - public function getEdgesTo(Vertex $endVertex) - { - if ($endVertex->getGraph() === $this->vertex->getGraph()) { - $map = $this->getEdgesMap(); - - if (isset($map[$endVertex->getId()])) { - return new Edges($map[$endVertex->getId()]); - } - } - throw new OutOfBoundsException('Given target vertex can not be reached from start vertex'); - } - - /** - * get map of vertex IDs to distance - * - * @return int[] - * @uses Vertex::hasLoop() - */ - public function getDistanceMap() - { - $ret = array(); - foreach ($this->getEdgesMap() as $vid => $edges) { - $ret[$vid] = count($edges); - } - - return $ret; - } - - /** - * get array of all target vertices this vertex has a path to - * - * @return Vertices - * @uses self::getEdgesMap() - */ - public function getVertices() - { - $ret = array(); - $graph = $this->vertex->getGraph(); - foreach ($this->getEdgesMap() as $vid => $unusedEdges) { - $ret[$vid] = $graph->getVertex($vid); - } - - return new Vertices($ret); - } - - public function getEdges() - { - $ret = array(); - foreach ($this->getEdgesMap() as $edges) { - foreach ($edges as $edge) { - if (!in_array($edge, $ret, true)) { - $ret []= $edge; - } - } - } - - return new Edges($ret); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/ShortestPath/Dijkstra.php b/lib/Fhaculty/Graph/Algorithm/ShortestPath/Dijkstra.php deleted file mode 100644 index 3600d7b3..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ShortestPath/Dijkstra.php +++ /dev/null @@ -1,121 +0,0 @@ -vertex->getId()] = INF; - - // just to get the cheapest vertex in the correct order - $cheapestVertex = new SplPriorityQueue(); - $cheapestVertex->insert($this->vertex, 0); - - // predecessor - $predecesVertexOfCheapestPathTo = Array(); - $predecesVertexOfCheapestPathTo[$this->vertex->getId()] = $this->vertex; - - // mark vertices when their cheapest path has been found - $usedVertices = Array(); - - $isFirst = true; - - // Repeat until all vertices have been marked - $totalCountOfVertices = count($this->vertex->getGraph()->getVertices()); - for ($i = 0; $i < $totalCountOfVertices; ++$i) { - $currentVertex = NULL; - $currentVertexId = NULL; - $isEmpty = false; - do { - // if the priority queue is empty there are isolated vertices, but the algorithm visited all other vertices - if ($cheapestVertex->isEmpty()) { - $isEmpty = true; - break; - } - // Get cheapest unmarked vertex - $currentVertex = $cheapestVertex->extract(); - $currentVertexId = $currentVertex->getId(); - // Vertices can be in the priority queue multiple times, with different path costs (if vertex is already marked, this is an old unvalid entry) - } while (isset($usedVertices[$currentVertexId])); - - // catch "algorithm ends" condition - if ($isEmpty) { - break; - } - - if ($isFirst) { - $isFirst = false; - } else { - // mark this vertex - $usedVertices[$currentVertexId] = true; - } - - // check for all edges of current vertex if there is a cheaper path (or IN OTHER WORDS: Add reachable nodes from currently added node and refresh the current possible distances) - foreach ($currentVertex->getEdgesOut() as $edge) { - $weight = $edge->getWeight(); - if ($weight < 0) { - throw new UnexpectedValueException('Djkstra not supported for negative weights - Consider using MooreBellmanFord'); - } - - $targetVertex = $edge->getVertexToFrom($currentVertex); - $targetVertexId = $targetVertex->getId(); - - // if the targetVertex is marked, the cheapest path for this vertex has already been found (no negative edges) { - if (!isset($usedVertices[$targetVertexId])) { - // calculate new cost to vertex - $newCostsToTargetVertex = $totalCostOfCheapestPathTo[$currentVertexId] + $weight; - if (is_infinite($newCostsToTargetVertex)) { - $newCostsToTargetVertex = $weight; - } - - if ((!isset($predecesVertexOfCheapestPathTo[$targetVertexId])) - // is the new path cheaper? - || $totalCostOfCheapestPathTo[$targetVertexId] > $newCostsToTargetVertex){ - - // Not an update, just an new insert with lower cost - $cheapestVertex->insert($targetVertex, - $newCostsToTargetVertex); - // so the lowest cost will be extraced first - // and higher cost will be skipped during extraction - - // update/set costs found with the new connection - $totalCostOfCheapestPathTo[$targetVertexId] = $newCostsToTargetVertex; - // update/set predecessor vertex from the new connection - $predecesVertexOfCheapestPathTo[$targetVertexId] = $currentVertex; - } - } - } - } - - if ($totalCostOfCheapestPathTo[$this->vertex->getId()] === INF) { - unset($predecesVertexOfCheapestPathTo[$this->vertex->getId()]); - } - - // algorithm is done, return resulting edges - return $this->getEdgesCheapestPredecesor($predecesVertexOfCheapestPathTo); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFord.php b/lib/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFord.php deleted file mode 100644 index 39b9a537..00000000 --- a/lib/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFord.php +++ /dev/null @@ -1,123 +0,0 @@ -getVerticesTarget() as $toVertex) { - $fromVertex = $edge->getVertexFromTo($toVertex); - - // If the fromVertex already has a path - if (isset($totalCostOfCheapestPathTo[$fromVertex->getId()])) { - // New possible costs of this path - $newCost = $totalCostOfCheapestPathTo[$fromVertex->getId()] + $edge->getWeight(); - if (is_infinite($newCost)) { - $newCost = $edge->getWeight() + 0; - } - - // No path has been found yet - if (!isset($totalCostOfCheapestPathTo[$toVertex->getId()]) - // OR this path is cheaper than the old path - || $totalCostOfCheapestPathTo[$toVertex->getId()] > $newCost){ - - $changed = $toVertex; - $totalCostOfCheapestPathTo[$toVertex->getId()] = $newCost; - $predecessorVertexOfCheapestPathTo[$toVertex->getId()] = $fromVertex; - } - } - } - } - - return $changed; - } - - /** - * Calculate the Moore-Bellman-Ford-Algorithm and get all edges on shortest path for this vertex - * - * @return Edges - * @throws NegativeCycleException if there is a negative cycle - */ - public function getEdges() - { - // start node distance, add placeholder weight - $totalCostOfCheapestPathTo = array($this->vertex->getId() => INF); - - // predecessor - $predecessorVertexOfCheapestPathTo = array($this->vertex->getId() => $this->vertex); - - // the usal algorithm says we repeat (n-1) times. - // but because we also want to check for loop edges on the start vertex, - // we have to add an additional step: - $numSteps = count($this->vertex->getGraph()->getVertices()); - $edges = $this->vertex->getGraph()->getEdges(); - $changed = true; - - for ($i = 0; $i < $numSteps && $changed; ++$i) { - $changed = $this->bigStep($edges, $totalCostOfCheapestPathTo, $predecessorVertexOfCheapestPathTo); - } - - // no cheaper edge to start vertex found => remove placeholder weight - if ($totalCostOfCheapestPathTo[$this->vertex->getId()] === INF) { - unset($predecessorVertexOfCheapestPathTo[$this->vertex->getId()]); - } - - // algorithm is done, build graph - $returnEdges = $this->getEdgesCheapestPredecesor($predecessorVertexOfCheapestPathTo); - - // Check for negative cycles (only if last step didn't already finish anyway) - // something is still changing... - if ($changed && $changed = $this->bigStep($edges, $totalCostOfCheapestPathTo, $predecessorVertexOfCheapestPathTo)) { - $cycle = Walk::factoryCycleFromPredecessorMap($predecessorVertexOfCheapestPathTo, $changed, Edges::ORDER_WEIGHT); - throw new NegativeCycleException('Negative cycle found', 0, NULL, $cycle); - } - - return $returnEdges; - } - - /** - * get negative cycle - * - * @return Walk - * @throws UnderflowException if there's no negative cycle - */ - public function getCycleNegative() - { - try { - $this->getEdges(); - } catch (NegativeCycleException $e) { - return $e->getCycle(); - } - throw new UnderflowException('No cycle found'); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Symmetric.php b/lib/Fhaculty/Graph/Algorithm/Symmetric.php deleted file mode 100644 index 13d7c0f0..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Symmetric.php +++ /dev/null @@ -1,44 +0,0 @@ -b there's also an edge b->a) - * - * @return boolean - * @uses Graph::getEdges() - * @uses EdgeDirected::getVertexStart() - * @uses EdgeDirected::getVertedEnd() - * @uses Vertex::hasEdgeTo() - */ - public function isSymmetric() - { - // check all edges - foreach ($this->graph->getEdges() as $edge) { - // only check directed edges (undirected ones are symmetric by definition) - if ($edge instanceof EdgeDirected) { - // check if end also has an edge to start - if (!$edge->getVertexEnd()->hasEdgeTo($edge->getVertexStart())) { - return false; - } - } - } - - return true; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/TopologicalSort.php b/lib/Fhaculty/Graph/Algorithm/TopologicalSort.php deleted file mode 100644 index 83038713..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TopologicalSort.php +++ /dev/null @@ -1,65 +0,0 @@ -graph->getVertices()->getVector()) as $vertex) { - $this->visit($vertex, $visited, $tsl); - } - - return new Vertices(array_reverse($tsl, true)); - } - - protected function visit(Vertex $vertex, array &$visited, array &$tsl) - { - $vid = $vertex->getId(); - if (isset($visited[$vid])) { - if ($visited[$vid] === false) { - // temporary mark => not a DAG - throw new UnexpectedValueException('Not a DAG'); - } - // otherwise already marked/visisted => no need to check again - } else { - // temporary mark - $visited[$vid] = false; - - foreach (array_reverse($vertex->getVerticesEdgeTo()->getVector()) as $v) { - $this->visit($v, $visited, $tsl); - } - - // mark as visited and include in result - $visited[$vid] = true; - $tsl[$vid] = $vertex; - } - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/TransposeGraph.php b/lib/Fhaculty/Graph/Algorithm/TransposeGraph.php deleted file mode 100644 index 2fae2351..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TransposeGraph.php +++ /dev/null @@ -1,36 +0,0 @@ -graph->createGraphCloneEdgeless(); - - foreach ($this->graph->getEdges() as $edge) { - if (!($edge instanceof EdgeDirected)) { - throw new UnexpectedValueException('Edge is undirected'); - } - $newgraph->createEdgeCloneInverted($edge); - } - - return $newgraph; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Base.php b/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Base.php deleted file mode 100644 index 2bfbfc13..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Base.php +++ /dev/null @@ -1,70 +0,0 @@ -getGraph()->createGraphCloneEdges($this->getEdges()); - } - - /** - * get graph this algorithm operates on - * - * @return Graph - */ - abstract protected function getGraph(); - - /** - * get start vertex this algorithm starts on - * - * @return Vertex - */ - abstract protected function getVertexStart(); - - /** - * get (first) best circle connecting all vertices - * - * @return Walk - * @uses AlgorithmTsp::getEdges() - * @uses AlgorithmTsp::getVertexStart() - * @uses Walk::factoryCycleFromEdges() - */ - public function getCycle() - { - return Walk::factoryCycleFromEdges($this->getEdges(), $this->getVertexStart()); - } - - public function getWeight() - { - $weight = 0; - foreach ($this->getEdges() as $edge) { - $weight += $edge->getWeight(); - } - - return $weight; - } - - /** - * get array of edges connecting all vertices in a circle - * - * @return Edges - */ - abstract public function getEdges(); -} diff --git a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Bruteforce.php b/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Bruteforce.php deleted file mode 100644 index e829acec..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/Bruteforce.php +++ /dev/null @@ -1,215 +0,0 @@ -graph = $graph; - } - - /** - * explicitly set upper limit to use for branch-and-bound - * - * this method can be used to optimize the algorithm by providing an upper - * bound of when to stop branching any further. - * - * @param double $limit - * @return AlgorithmTspBruteforce $this (chainable) - */ - public function setUpperLimit($limit) - { - $this->upperLimit = $limit; - - return $this; - } - - public function setUpperLimitMst() - { - $alg = new AlgorithmTspMst($this->graph); - $limit = $alg->createGraph()->getWeight(); - - return $this->setUpperLimit($limit); - } - - protected function getVertexStart() - { - // actual start doesn't really matter as we're only considering complete graphs here - return $this->graph->getVertices()->getVertexFirst(); - } - - protected function getGraph() - { - return $this->graph; - } - - /** - * get resulting (first) best circle of edges connecting all vertices - * - * @throws Exception on error - * @return Edges - */ - public function getEdges() - { - $this->numEdges = count($this->graph->getVertices()); - if ($this->numEdges < 3) { - throw new UnderflowException('Needs at least 3 vertices'); - } - - // numEdges 3-12 should work - - $this->bestWeight = $this->upperLimit; - $this->startVertex = $this->getVertexStart(); - - $result = $this->step($this->startVertex, - 0, - array(), - array() - ); - - if ($result === NULL) { - throw new UnexpectedValueException('No resulting solution for TSP found'); - } - - return new Edges($result); - } - - /** - * - * @param Vertex $vertex current point-of-view - * @param number $totalWeight total weight (so far) - * @param boolean[] $visitedVertices - * @param Edge[] $visitedEdges - * @return Edge[] - */ - private function step(Vertex $vertex, $totalWeight, array $visitedVertices, array $visitedEdges) - { - // stop recursion if best result is exceeded (branch and bound) - if ($this->branchAndBound && $this->bestWeight !== NULL && $totalWeight >= $this->bestWeight) { - return NULL; - } - // kreis geschlossen am Ende - if ($vertex === $this->startVertex && count($visitedEdges) === $this->numEdges) { - // new best result - $this->bestWeight = $totalWeight; - - return $visitedEdges; - } - - // only visit each vertex once - if (isset($visitedVertices[$vertex->getId()])) { - return NULL; - } - $visitedVertices[$vertex->getId()] = true; - - $bestResult = NULL; - - // weiter verzweigen in alle vertices - foreach ($vertex->getEdgesOut() as $edge) { - // get target vertex of this edge - $target = $edge->getVertexToFrom($vertex); - - $weight = $edge->getWeight(); - if ($weight < 0) { - throw new UnexpectedValueException('Edge with negative weight "' . $weight . '" not supported'); - } - - $result = $this->step($target, - $totalWeight + $weight, - $visitedVertices, - array_merge($visitedEdges, array($edge)) - ); - - // new result found - if ($result !== NULL) { - // branch and bound enabled (default): returned result MUST be the new best result - if($this->branchAndBound || - // this is the first result, just use it anyway - $bestResult === NULL || - // this is the new best result - $this->sumEdges($result) < $this->sumEdges($bestResult)){ - $bestResult = $result; - } - } - } - - return $bestResult; - } - - /** - * get sum of weight of given edges - * - * no need to optimize this further, as it's only evaluated if branchAndBound is disabled and - * there's no valid reason why anybody would want to do so. - * - * @param Edge[] $edges - * @return float - */ - private function sumEdges(array $edges) - { - $sum = 0; - foreach ($edges as $edge) { - $sum += $edge->getWeight(); - } - - return $sum; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/MinimumSpanningTree.php b/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/MinimumSpanningTree.php deleted file mode 100644 index e8049117..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/MinimumSpanningTree.php +++ /dev/null @@ -1,74 +0,0 @@ -graph = $inputGraph; - } - - protected function getVertexStart() - { - return $this->graph->getVertices()->getVertexFirst(); - } - - protected function getGraph() - { - return $this->graph; - } - - /** - * - * @return Edges - */ - public function getEdges() - { - $returnEdges = array(); - - // Create minimum spanning tree - $minimumSpanningTreeAlgorithm = new MstKruskal($this->graph); - $minimumSpanningTree = $minimumSpanningTreeAlgorithm->createGraph(); - - $alg = new SearchDepthFirst($minimumSpanningTree->getVertices()->getVertexFirst()); - // Depth first search in minmum spanning tree (for the eulerian path) - - $startVertex = NULL; - $oldVertex = NULL; - - // connect vertices in order of the depth first search - foreach ($alg->getVertices() as $vertex) { - - // get vertex from the original graph (not from the depth first search) - $vertex = $this->graph->getVertex($vertex->getId()); - // need to clone the edge from the original graph, therefore i need the original edge - if ($startVertex === NULL) { - $startVertex = $vertex; - } else { - // get edge(s) to clone, multiple edges are possible (returns an array if undirected edge) - $returnEdges []= $oldVertex->getEdgesTo($vertex)->getEdgeFirst(); - } - - $oldVertex = $vertex; - } - - // connect last vertex with start vertex - // multiple edges are possible (returns an array if undirected edge) - $returnEdges []= $oldVertex->getEdgesTo($startVertex)->getEdgeFirst(); - - return new Edges($returnEdges); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/NearestNeighbor.php b/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/NearestNeighbor.php deleted file mode 100644 index e198045a..00000000 --- a/lib/Fhaculty/Graph/Algorithm/TravelingSalesmanProblem/NearestNeighbor.php +++ /dev/null @@ -1,93 +0,0 @@ -vertex = $startVertex; - } - - protected function getVertexStart() - { - return $this->vertex; - } - - protected function getGraph() - { - return $this->vertex->getGraph(); - } - - /** - * - * @return Edges - */ - public function getEdges() - { - $returnEdges = array(); - - $n = count($this->vertex->getGraph()->getVertices()); - - $vertex = $this->vertex; - $visitedVertices = array($vertex->getId() => true); - - for ($i = 0; $i < $n - 1; ++$i, - // n-1 steps (spanning tree) - $vertex = $nextVertex) { - - // get all edges from the aktuel vertex - $edges = $vertex->getEdgesOut(); - - $sortedEdges = new SplPriorityQueue(); - - // sort the edges - foreach ($edges as $edge) { - $sortedEdges->insert($edge, - $edge->getWeight()); - } - - // Untill first is found: get cheepest edge - foreach ($sortedEdges as $edge) { - - // Get EndVertex of this edge - $nextVertex = $edge->getVertexToFrom($vertex); - - // is unvisited - if (!isset($visitedVertices[$nextVertex->getId()])) { - break; - } - } - - // check if there is a way i can use - if (isset($visitedVertices[$nextVertex->getId()])) { - throw new UnexpectedValueException('Graph is not complete - can\'t find an edge to unconnected vertex'); - } - - $visitedVertices[$nextVertex->getId()] = TRUE; - - // clone edge in new Graph - $returnEdges []= $edge; - - } - - // check if there is a way from end edge to start edge - // get first connecting edge - // connect the last vertex with the start vertex - $returnEdges []= $vertex->getEdgesTo($this->vertex)->getEdgeFirst(); - - return new Edges($returnEdges); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Tree/Base.php b/lib/Fhaculty/Graph/Algorithm/Tree/Base.php deleted file mode 100644 index 1dbfb675..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Tree/Base.php +++ /dev/null @@ -1,107 +0,0 @@ -degree = new Degree($graph); - } - - /** - * checks whether the given graph is actually a tree - * - * @return boolean - */ - abstract public function isTree(); - - /** - * checks if the given $vertex is a leaf (outermost vertext) - * - * leaf vertex is also known as leaf node, external node or terminal node - * - * @param Vertex $vertex - * @return boolean - */ - abstract public function isVertexLeaf(Vertex $vertex); - - /** - * checks if the given $vertex is an internal vertex (somewhere in the "middle" of the tree) - * - * internal vertex is also known as inner node (inode) or branch node - * - * @param Vertex $vertex - * @return boolean - */ - abstract public function isVertexInternal(Vertex $vertex); - - /** - * get array of leaf vertices (outermost vertices with no children) - * - * @return Vertices - * @uses Graph::getVertices() - * @uses self::isVertexLeaf() - */ - public function getVerticesLeaf() - { - return $this->graph->getVertices()->getVerticesMatch(array($this, 'isVertexLeaf')); - } - - /** - * get array of internal vertices - * - * @return Vertices - * @uses Graph::getVertices() - * @uses self::isVertexInternal() - */ - public function getVerticesInternal() - { - return $this->graph->getVertices()->getVerticesMatch(array($this, 'isVertexInternal')); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Tree/BaseDirected.php b/lib/Fhaculty/Graph/Algorithm/Tree/BaseDirected.php deleted file mode 100644 index ef557793..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Tree/BaseDirected.php +++ /dev/null @@ -1,325 +0,0 @@ - B - * \ - * \--> C - * - * - Alternative InTree implementation where Edges "point towards" root Vertex - * - * ROOT - * ^ ^ - * / \ - * A B - * ^ - * \ - * C - * - * It's your choice on how to direct the edges, but make sure they all point in - * the "same direction", or it will not be a valid tree anymore. However your - * decision may be, in the above example, ROOT is always the root Vertex, - * B is the parent of "C" and A, B are the children of ROOT. - * - * For performance reasons, except for `isTree()`, none of the below methods - * check if the given Graph is actually a valid tree. So make sure to verify - * `isTree()` returns `true` before relying on any of the methods. - * - * @link http://en.wikipedia.org/wiki/Arborescence_%28graph_theory%29 - * @link http://en.wikipedia.org/wiki/Spaghetti_stack - * @see OutTree usual implementation where Edges "point away" from root vertex - * @see InTree alternative implementation where Edges "point towards" root vertex - */ -abstract class BaseDirected extends Tree -{ - /** - * get root vertex for this in-tree - * - * @return Vertex - * @throws UnderflowException if given graph is empty or no possible root candidate was found (check isTree()!) - * @uses Graph::getVertices() to iterate over each Vertex - * @uses self::isVertexPossibleRoot() to check if any Vertex is a possible root candidate - */ - public function getVertexRoot() - { - foreach ($this->graph->getVertices() as $vertex) { - if ($this->isVertexPossibleRoot($vertex)) { - return $vertex; - } - } - throw new UnderflowException('No possible root found. Either empty graph or no Vertex with proper degree found.'); - } - - /** - * checks if this is a tree - * - * @return boolean - * @uses self::getVertexRoot() to get root Vertex to start search from - * @uses self::getVerticesSubtree() to count number of vertices connected to root - */ - public function isTree() - { - try { - $root = $this->getVertexRoot(); - } - catch (UnderflowException $e) { - return false; - } - catch (UnexpectedValueException $e) { - return false; - } - - try { - $num = count($this->getVerticesSubtree($root)); - } - catch (UnexpectedValueException $e) { - return false; - } - - // check number of vertices reachable from root should match total number of vertices - return ($num === count($this->graph->getVertices())); - } - - /** - * get parent vertex for given $vertex - * - * @param Vertex $vertex - * @throws UnderflowException if vertex has no parent (is a root vertex) - * @throws UnexpectedValueException if vertex has more than one possible parent (check isTree()!) - * @return Vertex - * @uses self::getVerticesParents() to get array of parent vertices - */ - public function getVertexParent(Vertex $vertex) - { - $parents = $this->getVerticesParent($vertex); - if (count($parents) !== 1) { - if ($parents->isEmpty()) { - throw new UnderflowException('No parents for given vertex found'); - } else { - throw new UnexpectedValueException('More than one parent'); - } - } - return $parents->getVertexFirst(); - } - - /** - * get array of child vertices for given $vertex - * - * @param Vertex $vertex - * @return Vertices - * @throws UnexpectedValueException if the given $vertex contains invalid / parallel edges (check isTree()!) - */ - abstract public function getVerticesChildren(Vertex $vertex); - - /** - * internal helper to get all parents vertices - * - * a valid tree vertex only ever has a single parent, except for the root, - * which has none. - * - * @param Vertex $vertex - * @return Vertices - * @throws UnexpectedValueException if the given $vertex contains invalid / parallel edges (check isTree()!) - */ - abstract protected function getVerticesParent(Vertex $vertex); - - /** - * check if given vertex is a possible root (i.e. has no parent) - * - * @param Vertex $vertex - * @return boolean - * @uses self::getVerticesParent() - */ - protected function isVertexPossibleRoot(Vertex $vertex) - { - return (count($this->getVerticesParent($vertex)) === 0); - } - - /** - * checks if the given $vertex is a leaf (outermost vertex with no children) - * - * @param Vertex $vertex - * @return boolean - * @uses self::getVerticesChildren() to check given vertex has no children - */ - public function isVertexLeaf(Vertex $vertex) - { - return (count($this->getVerticesChildren($vertex)) === 0); - } - - /** - * checks if the given $vertex is an internal vertex (has children and is not root) - * - * @param Vertex $vertex - * @return boolean - * @uses self::getVerticesParent() to check given vertex has a parent (is not root) - * @uses self::getVerticesChildren() to check given vertex has children (is not a leaf) - * @see \Fhaculty\Graph\Algorithm\Tree\Base::isVertexInternal() for more information - */ - public function isVertexInternal(Vertex $vertex) - { - return (!$this->getVerticesParent($vertex)->isEmpty() && !$this->getVerticesChildren($vertex)->isEmpty()); - } - - /** - * get degree of tree (maximum number of children) - * - * @return int - * @throws UnderflowException for empty graphs - * @uses Graph::getVertices() - * @uses self::getVerticesChildren() - */ - public function getDegree() - { - $max = null; - foreach ($this->graph->getVertices() as $vertex) { - $num = count($this->getVerticesChildren($vertex)); - if ($max === null || $num > $max) { - $max = $num; - } - } - if ($max === null) { - throw new UnderflowException('No vertices found'); - } - return $max; - } - - /** - * get depth of given $vertex (number of edges between root vertex) - * - * root has depth zero - * - * @param Vertex $vertex - * @return int - * @throws UnderflowException for empty graphs - * @throws UnexpectedValueException if there's no path to root node (check isTree()!) - * @uses self::getVertexRoot() - * @uses self::getVertexParent() for each step - */ - public function getDepthVertex(Vertex $vertex) - { - $root = $this->getVertexRoot(); - - $depth = 0; - while ($vertex !== $root) { - $vertex = $this->getVertexParent($vertex); - ++$depth; - } - return $depth; - } - - /** - * get height of this tree (longest downward path to a leaf) - * - * a single vertex graph has height zero - * - * @return int - * @throws UnderflowException for empty graph - * @uses self::getVertexRoot() - * @uses self::getHeightVertex() - */ - public function getHeight() - { - return $this->getHeightVertex($this->getVertexRoot()); - } - - /** - * get height of given vertex (longest downward path to a leaf) - * - * leafs has height zero - * - * @param Vertex $vertex - * @return int - * @uses self::getVerticesChildren() to get children of given vertex - * @uses self::getHeightVertex() to recurse into sub-children - */ - public function getHeightVertex(Vertex $vertex) - { - $max = 0; - foreach ($this->getVerticesChildren($vertex) as $vertex) { - $height = $this->getHeightVertex($vertex) + 1; - if ($height > $max) { - $max = $height; - } - } - return $max; - } - - /** - * get all vertices that are in the subtree of the given $vertex (which IS included) - * - * root vertex will return the whole tree, leaf vertices will only return themselves - * - * @param Vertex $vertex - * @throws UnexpectedValueException if there are invalid edges (check isTree()!) - * @return Vertices - * @uses self::getVerticesSubtreeRecursive() - * @uses self::getVerticesSubtree() - */ - public function getVerticesSubtree(Vertex $vertex) - { - $vertices = array(); - $this->getVerticesSubtreeRecursive($vertex, $vertices); - - return new Vertices($vertices); - } - - /** - * helper method to get recursively get subtree for given $vertex - * - * @param Vertex $vertex - * @param Vertex[] $vertices - * @throws UnexpectedValueException if multiple links were found to the given edge (check isTree()!) - * @uses self::getVerticesChildren() - * @uses self::getVerticesSubtreeRecursive() to recurse into subtrees - */ - private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices) - { - $vid = $vertex->getId(); - if (isset($vertices[$vid])) { - throw new UnexpectedValueException('Multiple links found'); - } - $vertices[$vid] = $vertex; - - foreach ($this->getVerticesChildren($vertex) as $vertexChild) { - $this->getVerticesSubtreeRecursive($vertexChild, $vertices); - } - } - - /** - * get all vertices below the given $vertex (which is NOT included) - * - * think of this as the recursive version of getVerticesChildren() - * - * @param Vertex $vertex - * @return Vertices - * @throws UnexpectedValueException if there are invalid edges (check isTree()!) - * @uses self::getVerticesSubtree() - */ - public function getVerticesDescendant(Vertex $vertex) - { - $vertices = $this->getVerticesSubtree($vertex)->getMap(); - unset($vertices[$vertex->getId()]); - - return new Vertices($vertices); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Tree/InTree.php b/lib/Fhaculty/Graph/Algorithm/Tree/InTree.php deleted file mode 100644 index 3b7765af..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Tree/InTree.php +++ /dev/null @@ -1,44 +0,0 @@ -getVerticesEdgeFrom(); - if ($vertices->hasDuplicates()) { - throw new UnexpectedValueException(); - } - - return $vertices; - } - - protected function getVerticesParent(Vertex $vertex) - { - $vertices = $vertex->getVerticesEdgeTo(); - if ($vertices->hasDuplicates()) { - throw new UnexpectedValueException(); - } - - return $vertices; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Tree/OutTree.php b/lib/Fhaculty/Graph/Algorithm/Tree/OutTree.php deleted file mode 100644 index 9ac0c0b5..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Tree/OutTree.php +++ /dev/null @@ -1,44 +0,0 @@ - B - * \ - * \--> C - * - * also known as arborescence - * - * @link http://en.wikipedia.org/wiki/Arborescence_%28graph_theory%29 - * @see DirectedTree for more information on directed, rooted trees - */ -class OutTree extends DirectedTree -{ - public function getVerticesChildren(Vertex $vertex) - { - $vertices = $vertex->getVerticesEdgeTo(); - if ($vertices->hasDuplicates()) { - throw new UnexpectedValueException(); - } - - return $vertices; - } - - protected function getVerticesParent(Vertex $vertex) - { - $vertices = $vertex->getVerticesEdgeFrom(); - if ($vertices->hasDuplicates()) { - throw new UnexpectedValueException(); - } - - return $vertices; - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Tree/Undirected.php b/lib/Fhaculty/Graph/Algorithm/Tree/Undirected.php deleted file mode 100644 index f5fc3cd8..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Tree/Undirected.php +++ /dev/null @@ -1,150 +0,0 @@ -graph->getVertices()->isEmpty()) { - return false; - } - - // every vertex can represent a root vertex, so just pick one - $root = $this->graph->getVertices()->getVertexFirst(); - - $vertices = array(); - try { - $this->getVerticesSubtreeRecursive($root, $vertices, null); - } - catch (UnexpectedValueException $e) { - return false; - } - - return (count($vertices) === count($this->graph->getVertices())); - } - - /** - * checks if the given $vertex is a leaf (outermost vertex with exactly one edge) - * - * @param Vertex $vertex - * @return boolean - * @uses Degree::getDegreeVertex() - */ - public function isVertexLeaf(Vertex $vertex) - { - return ($this->degree->getDegreeVertex($vertex) === 1); - } - - /** - * checks if the given $vertex is an internal vertex (inner vertex with at least 2 edges) - * - * @param Vertex $vertex - * @return boolean - * @uses Degree::getDegreeVertex() - */ - public function isVertexInternal(Vertex $vertex) - { - return ($this->degree->getDegreeVertex($vertex) >= 2); - } - - /** - * get subtree for given Vertex and ignore path to "parent" ignoreVertex - * - * @param Vertex $vertex - * @param Vertex[] $vertices - * @param Vertex|null $ignore - * @throws UnexpectedValueException for cycles or directed edges (check isTree()!) - * @uses self::getVerticesNeighbor() - * @uses self::getVerticesSubtreeRecursive() to recurse into sub-subtrees - */ - private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices, Vertex $ignore = null) - { - if (isset($vertices[$vertex->getId()])) { - // vertex already visited => must be a cycle - throw new UnexpectedValueException('Vertex already visited'); - } - $vertices[$vertex->getId()] = $vertex; - - foreach ($this->getVerticesNeighbor($vertex) as $vertexNeighboor) { - if ($vertexNeighboor === $ignore) { - // ignore source vertex only once - $ignore = null; - continue; - } - $this->getVerticesSubtreeRecursive($vertexNeighboor, $vertices, $vertex); - } - } - - /** - * get neighbor vertices for given start vertex - * - * @param Vertex $vertex - * @throws UnexpectedValueException for directed edges - * @return Vertices (might include possible duplicates) - * @uses Vertex::getEdges() - * @uses Edge::getVertexToFrom() - * @see Vertex::getVerticesEdge() - */ - private function getVerticesNeighbor(Vertex $vertex) - { - $vertices = array(); - foreach ($vertex->getEdges() as $edge) { - /* @var Edge $edge */ - if (!($edge instanceof UndirectedEdge)) { - throw new UnexpectedValueException('Directed edge encountered'); - } - $vertices[] = $edge->getVertexToFrom($vertex); - } - return new Vertices($vertices); - } -} diff --git a/lib/Fhaculty/Graph/Algorithm/Weight.php b/lib/Fhaculty/Graph/Algorithm/Weight.php deleted file mode 100644 index 38445635..00000000 --- a/lib/Fhaculty/Graph/Algorithm/Weight.php +++ /dev/null @@ -1,107 +0,0 @@ -set->getEdges() as $edge) { - if ($edge->getWeight() !== NULL) { - return true; - } - } - - return false; - } - - /** - * get total weight of graph (sum of weight of all edges) - * - * edges with no weight assigned will evaluate to weight (int) 0. thus an - * unweighted graph (see isWeighted()) will return total weight of (int) 0. - * - * returned weight can also be negative or (int) 0 if edges have been - * assigned a negative weight or a weight of (int) 0. - * - * @return float total weight - * @see self::isWeighted() - * @uses Edge::getWeight() - */ - public function getWeight() - { - $weight = 0; - foreach ($this->set->getEdges() as $edge) { - $w = $edge->getWeight(); - if ($w !== NULL) { - $weight += $w; - } - } - - return $weight; - } - - /** - * get minimum weight assigned to all edges - * - * minimum weight is often needed because some algorithms do not support - * negative weights or edges with zero weight. - * - * edges with NO (null) weight will NOT be considered for the minimum weight. - * - * @return float|NULL minimum edge weight or NULL if graph is not weighted or empty - * @uses Edge::getWeight() - */ - public function getWeightMin() - { - $min = NULL; - foreach ($this->set->getEdges() as $edge) { - $weight = $edge->getWeight(); - if ($weight !== null && ($min === NULL || $weight < $min)) { - $min = $weight; - } - } - - return $min; - } - - /** - * get total weight of current flow (sum of all edges flow(e) * weight(e)) - * - * @return float - * @see Graph::getWeight() to just get the sum of all edges' weights - * @uses Edge::getFlow() - * @uses Edge::getWeight() - */ - public function getWeightFlow() - { - $sum = 0; - foreach ($this->set->getEdges() as $edge) { - $sum += $edge->getFlow() * $edge->getWeight(); - } - - return $sum; - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/BipartitTest.php b/tests/Fhaculty/Graph/Algorithm/BipartitTest.php deleted file mode 100644 index df520974..00000000 --- a/tests/Fhaculty/Graph/Algorithm/BipartitTest.php +++ /dev/null @@ -1,94 +0,0 @@ -assertTrue($alg->isBipartit()); - $this->assertEquals(array(), $alg->getColors()); - $this->assertEquals(array(0 => array(), 1 => array()), $alg->getColorVertices()); - } - - public function testGraphPairIsBipartit() - { - // 1 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v1->createEdgeTo($v2); - - $alg = new AlgorithmBipartit($graph); - - $this->assertTrue($alg->isBipartit()); - $this->assertEquals(array(1 => 0, 2 => 1), $alg->getColors()); - $this->assertEquals(array(0 => array(1 => $v1), 1 => array(2 => $v2)), $alg->getColorVertices()); - - return $alg; - } - - /** - * - * @param AlgorithmBipartit $alg - * @depends testGraphPairIsBipartit - */ - public function testGraphPairBipartitGroups(AlgorithmBipartit $alg) - { - // graph does not have any groups assigned, so its groups are not bipartit - $this->assertFalse($alg->isBipartitGroups()); - - // create a cloned graph with groups assigned according to bipartition - $graph = $alg->createGraphGroups(); - - $this->assertInstanceOf('Fhaculty\Graph\Graph', $graph); - - $alg2 = new AlgorithmBipartit($graph); - $this->assertTrue($alg2->isBipartitGroups()); - } - - public function testGraphTriangleCycleIsNotBipartit() - { - // 1 -> 2 --> 3 --> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v1->createEdgeTo($v2); - $v2->createEdgeTo($v3); - $v3->createEdgeTo($v1); - - $alg = new AlgorithmBipartit($graph); - - $this->assertFalse($alg->isBipartit()); - - return $alg; - } - - /** - * - * @param AlgorithmBipartit $alg - * @expectedException UnexpectedValueException - * @depends testGraphTriangleCycleIsNotBipartit - */ - public function testGraphTriangleCycleColorsInvalid(AlgorithmBipartit $alg) - { - $alg->getColors(); - } - - /** - * - * @param AlgorithmBipartit $alg - * @expectedException UnexpectedValueException - * @depends testGraphTriangleCycleIsNotBipartit - */ - public function testGraphTriangleCycleColorVerticesInvalid(AlgorithmBipartit $alg) - { - $alg->getColorVertices(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/CompleteTest.php b/tests/Fhaculty/Graph/Algorithm/CompleteTest.php deleted file mode 100644 index 4c6346df..00000000 --- a/tests/Fhaculty/Graph/Algorithm/CompleteTest.php +++ /dev/null @@ -1,65 +0,0 @@ -assertTrue($alg->isComplete()); - } - - public function testGraphSingleTrivialK1() - { - $graph = new Graph(); - $graph->createVertex(1); - - $alg = new AlgorithmComplete($graph); - - $this->assertTrue($alg->isComplete()); - } - - public function testGraphSimplePairK2() - { - // 1 -- 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - - $alg = new AlgorithmComplete($graph); - - $this->assertTrue($alg->isComplete()); - } - - public function testGraphSingleDirectedIsNotComplete() - { - // 1 -> 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - - $alg = new AlgorithmComplete($graph); - - $this->assertFalse($alg->isComplete()); - } - - public function testAdditionalEdgesToNotAffectCompleteness() - { - // 1 -> 2 - // 1 -- 2 - // 2 -> 1 - // 1 -> 1 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - $graph->getVertex(1)->createEdge($graph->getVertex(2)); - $graph->getVertex(2)->createEdgeTo($graph->getVertex(1)); - $graph->getVertex(1)->createEdgeTo($graph->getVertex(1)); - - $alg = new AlgorithmComplete($graph); - - $this->assertTrue($alg->isComplete()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/ConnectedComponentsTest.php b/tests/Fhaculty/Graph/Algorithm/ConnectedComponentsTest.php deleted file mode 100644 index 008059b8..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ConnectedComponentsTest.php +++ /dev/null @@ -1,97 +0,0 @@ -assertEquals(0, $alg->getNumberOfComponents()); - $this->assertFalse($alg->isSingle()); - $this->assertCount(0, $alg->createGraphsComponents()); - } - - public function testGraphSingleTrivial() - { - $graph = new Graph(); - $graph->createVertex(1); - - $alg = new AlgorithmConnected($graph); - - $this->assertEquals(1, $alg->getNumberOfComponents()); - $this->assertTrue($alg->isSingle()); - - $graphs = $alg->createGraphsComponents(); - - $this->assertCount(1, $graphs); - $this->assertGraphEquals($graph, reset($graphs)); - } - - public function testGraphEdgeDirections() - { - // 1 -- 2 -> 3 <- 4 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - $graph->getVertex(2)->createEdgeTo($graph->createVertex(3)); - $graph->createVertex(4)->createEdgeTo($graph->getVertex(3)); - - $alg = new AlgorithmConnected($graph); - - $this->assertEquals(1, $alg->getNumberOfComponents()); - $this->assertTrue($alg->isSingle()); - - $graphs = $alg->createGraphsComponents(); - - $this->assertCount(1, $graphs); - $this->assertGraphEquals($graph, reset($graphs)); - $this->assertGraphEquals($graph, $alg->createGraphComponentVertex($graph->getVertex(1))); - } - - public function testComponents() - { - // 1 -- 2, 3 -> 4, 5 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $v5 = $graph->createVertex(5); - $e1 = $v1->createEdge($v2); - $e2 = $v3->createEdgeTo($v4); - - $alg = new AlgorithmConnected($graph); - - $this->assertEquals(3, $alg->getNumberOfComponents()); - $this->assertFalse($alg->isSingle()); - - $graphs = $alg->createGraphsComponents(); - $this->assertCount(3, $graphs); - - $ge = new Graph(); - $ge->createVertex(1)->createEdge($ge->createVertex(2)); - $this->assertGraphEquals($ge, $alg->createGraphComponentVertex($v2)); - - $ge = new Graph(); - $ge->createVertex(5); - $this->assertEquals($ge, $alg->createGraphComponentVertex($v5)); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testInvalidVertexPassedToAlgorithm() - { - $graph = new Graph(); - - $graph2 = new Graph(); - $v2 = $graph2->createVertex(12); - - $alg = new AlgorithmConnected($graph); - $alg->createGraphComponentVertex($v2); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/DegreeTest.php b/tests/Fhaculty/Graph/Algorithm/DegreeTest.php deleted file mode 100644 index f1768539..00000000 --- a/tests/Fhaculty/Graph/Algorithm/DegreeTest.php +++ /dev/null @@ -1,100 +0,0 @@ -getDegree(); - $this->fail(); - } - catch (UnderflowException $e) { } - - try { - $alg->getDegreeMin(); - $this->fail(); - } - catch (UnderflowException $e) { } - - try { - $alg->getDegreeMax(); - $this->fail(); - } - catch (UnderflowException $e) { } - - $this->assertTrue($alg->isRegular()); - $this->assertTrue($alg->isBalanced()); - } - - public function testGraphIsolated() - { - $graph = new Graph(); - $graph->createVertex(1); - $graph->createVertex(2); - - $alg = new AlgorithmDegree($graph); - - $this->assertEquals(0, $alg->getDegree()); - $this->assertEquals(0, $alg->getDegreeMin()); - $this->assertEquals(0, $alg->getDegreeMax()); - $this->assertTrue($alg->isRegular()); - $this->assertTrue($alg->isBalanced()); - } - - public function testGraphIrregular() - { - // 1 -> 2 -> 3 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v1->createEdgeTo($v2); - $v2->createEdgeTo($v3); - - $alg = new AlgorithmDegree($graph); - - try { - $this->assertEquals(0, $alg->getDegree()); - $this->fail(); - } - catch (UnexpectedValueException $e) { } - - $this->assertEquals(1, $alg->getDegreeMin()); - $this->assertEquals(2, $alg->getDegreeMax()); - $this->assertFalse($alg->isRegular()); - $this->assertFalse($alg->isBalanced()); - - - $this->assertEquals(0, $alg->getDegreeInVertex($v1)); - $this->assertEquals(1, $alg->getDegreeOutVertex($v1)); - $this->assertEquals(1, $alg->getDegreeVertex($v1)); - $this->assertFalse($alg->isVertexIsolated($v1)); - $this->assertFalse($alg->isVertexSink($v1)); - $this->assertTrue($alg->isVertexSource($v1)); - - $this->assertEquals(1, $alg->getDegreeInVertex($v2)); - $this->assertEquals(1, $alg->getDegreeOutVertex($v2)); - $this->assertEquals(2, $alg->getDegreeVertex($v2)); - $this->assertFalse($alg->isVertexIsolated($v2)); - $this->assertFalse($alg->isVertexSink($v2)); - $this->assertFalse($alg->isVertexSource($v2)); - - $this->assertEquals(1, $alg->getDegreeInVertex($v3)); - $this->assertEquals(0, $alg->getDegreeOutVertex($v3)); - $this->assertEquals(1, $alg->getDegreeVertex($v3)); - $this->assertFalse($alg->isVertexIsolated($v3)); - $this->assertTrue($alg->isVertexSink($v3)); - $this->assertFalse($alg->isVertexSource($v3)); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/DetectNegativeCycleTest.php b/tests/Fhaculty/Graph/Algorithm/DetectNegativeCycleTest.php deleted file mode 100644 index 66189b10..00000000 --- a/tests/Fhaculty/Graph/Algorithm/DetectNegativeCycleTest.php +++ /dev/null @@ -1,151 +0,0 @@ -assertFalse($alg->hasCycleNegative()); - - return $alg; - } - - /** - * - * @param DetectNegativeCycle $alg - * @depends testNullGraph - * @expectedException UnderflowException - */ - public function testNullGraphHasNoCycle(DetectNegativeCycle $alg) - { - $alg->getCycleNegative(); - } - - /** - * - * @param DetectNegativeCycle $alg - * @depends testNullGraph - * @expectedException UnderflowException - */ - public function testNullGraphHasNoCycleGraph(DetectNegativeCycle $alg) - { - $alg->createGraph(); - } - - public function testNegativeLoop() - { - // 1 --[-1]--> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $e1 = $v1->createEdgeTo($v1)->setWeight(-1); - - $alg = new DetectNegativeCycle($graph); - - $this->assertTrue($alg->hasCycleNegative()); - - $cycle = $alg->getCycleNegative(); - - $this->assertCount(1, $cycle->getEdges()); - $this->assertCount(2, $cycle->getVertices()); - $this->assertEquals($e1, $cycle->getEdges()->getEdgeFirst()); - $this->assertEquals($v1, $cycle->getVertices()->getVertexFirst()); - } - - public function testNegativeCycle() - { - // 1 --[-1]--> 2 - // ^ | - // \---[-2]----/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2)->setWeight(-1); - $e2 = $v2->createEdgeTo($v1)->setWeight(-2); - - $alg = new DetectNegativeCycle($graph); - - $this->assertTrue($alg->hasCycleNegative()); - - $cycle = $alg->getCycleNegative(); - - $this->assertCount(2, $cycle->getEdges()); - $this->assertCount(3, $cycle->getVertices()); - } - - public function testNegativeUndirectedIsNegativeCycle() - { - // 1 --[-1]-- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdge($v2)->setWeight(-1); - - $alg = new DetectNegativeCycle($graph); - - $this->assertTrue($alg->hasCycleNegative()); - - $cycle = $alg->getCycleNegative(); - - $this->assertCount(2, $cycle->getEdges()); - $this->assertCount(3, $cycle->getVertices()); - } - - public function testNegativeCycleSubgraph() - { - // 1 --[1]--> 2 --[1]--> 3 --[1]--> 4 - // ^ | - // \---[-2]---/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $e1 = $v1->createEdgeTo($v2)->setWeight(1); - $e2 = $v2->createEdgeTo($v3)->setWeight(1); - $e3 = $v3->createEdgeTo($v4)->setWeight(1); - $e4 = $v4->createEdgeTo($v3)->setWeight(-2); - - $alg = new DetectNegativeCycle($graph); - - $this->assertTrue($alg->hasCycleNegative()); - - $cycle = $alg->getCycleNegative(); - - $this->assertCount(2, $cycle->getEdges()); - $this->assertCount(3, $cycle->getVertices()); - $this->assertTrue($cycle->getVertices()->hasVertexId(3)); - $this->assertTrue($cycle->getVertices()->hasVertexId(4)); - } - - public function testNegativeComponents() - { - // 1 -- 2 3 --[-1]--> 4 - // ^ | - // \---[-2]----/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $e1 = $v1->createEdge($v2); - $e2 = $v3->createEdgeTo($v4)->setWeight(-1); - $e3 = $v4->createEdgeTo($v3)->setWeight(-2); - - $alg = new DetectNegativeCycle($graph); - - $this->assertTrue($alg->hasCycleNegative()); - - $cycle = $alg->getCycleNegative(); - - $this->assertCount(2, $cycle->getEdges()); - $this->assertCount(3, $cycle->getVertices()); - $this->assertTrue($cycle->getVertices()->hasVertexId(3)); - $this->assertTrue($cycle->getVertices()->hasVertexId(4)); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/DirectedTest.php b/tests/Fhaculty/Graph/Algorithm/DirectedTest.php deleted file mode 100644 index 6e246e8b..00000000 --- a/tests/Fhaculty/Graph/Algorithm/DirectedTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertFalse($alg->hasDirected()); - $this->assertFalse($alg->hasUndirected()); - $this->assertFalse($alg->isMixed()); - } - - public function testGraphUndirected() - { - // 1 -- 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - - $alg = new AlgorithmDirected($graph); - - $this->assertFalse($alg->hasDirected()); - $this->assertTrue($alg->hasUndirected()); - $this->assertFalse($alg->isMixed()); - } - - public function testGraphDirected() - { - // 1 -> 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - - $alg = new AlgorithmDirected($graph); - - $this->assertTrue($alg->hasDirected()); - $this->assertFalse($alg->hasUndirected()); - $this->assertFalse($alg->isMixed()); - } - - public function testGraphMixed() - { - // 1 -- 2 -> 3 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - $graph->getVertex(2)->createEdgeTo($graph->createVertex(3)); - - $alg = new AlgorithmDirected($graph); - - $this->assertTrue($alg->hasDirected()); - $this->assertTrue($alg->hasUndirected()); - $this->assertTrue($alg->isMixed()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/EulerianTest.php b/tests/Fhaculty/Graph/Algorithm/EulerianTest.php deleted file mode 100644 index 09e43feb..00000000 --- a/tests/Fhaculty/Graph/Algorithm/EulerianTest.php +++ /dev/null @@ -1,45 +0,0 @@ -assertFalse($alg->hasCycle()); - } - - public function testGraphPairHasNoCycle() - { - // 1 -- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v1->createEdge($v2); - - $alg = new AlgorithmEulerian($graph); - - $this->assertFalse($alg->hasCycle()); - } - - public function testGraphTriangleCycleIsNotBipartit() - { - // 1 -- 2 -- 3 -- 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v1->createEdge($v2); - $v2->createEdge($v3); - $v3->createEdge($v1); - - $alg = new AlgorithmEulerian($graph); - - $this->assertTrue($alg->hasCycle()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/FlowTest.php b/tests/Fhaculty/Graph/Algorithm/FlowTest.php deleted file mode 100644 index 37ef400c..00000000 --- a/tests/Fhaculty/Graph/Algorithm/FlowTest.php +++ /dev/null @@ -1,98 +0,0 @@ -assertFalse($alg->hasFlow()); - $this->assertEquals(0, $alg->getBalance()); - $this->assertTrue($alg->isBalancedFlow()); - - return $graph; - } - - public function testEdgeWithZeroFlowIsConsideredFlow() - { - // 1 -> 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2))->setFlow(0); - - - $alg = new AlgorithmFlow($graph); - - $this->assertTrue($alg->hasFlow()); - $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1))); - $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2))); - } - - /** - * - * @param Graph $graph - * @depends testGraphEmpty - */ - public function testGraphSimple(Graph $graph) - { - // 1 -> 2 - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - - $alg = new AlgorithmFlow($graph); - - $this->assertFalse($alg->hasFlow()); - $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1))); - $this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2))); - - return $graph; - } - - /** - * - * @param Graph $graph - * @depends testGraphSimple - */ - public function testGraphWithUnweightedEdges(Graph $graph) - { - // additional flow edge: 2 -> 3 - $graph->getVertex(2)->createEdgeTo($graph->createVertex(3))->setFlow(10); - - $alg = new AlgorithmFlow($graph); - - $this->assertTrue($alg->hasFlow()); - $this->assertEquals(10, $alg->getFlowVertex($graph->getVertex(2))); - $this->assertEquals(-10, $alg->getFlowVertex($graph->getVertex(3))); - } - - public function testGraphBalance() - { - // source(+100) -> sink(-10) - $graph = new Graph(); - $graph->createVertex('source')->setBalance(100); - $graph->createVertex('sink')->setBalance(-10); - - $alg = new AlgorithmFlow($graph); - - $this->assertEquals(90, $alg->getBalance()); - $this->assertFalse($alg->isBalancedFlow()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testVertexWithUndirectedEdgeHasInvalidFlow() - { - // 1 -- 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2))->setFlow(10); - - - $alg = new AlgorithmFlow($graph); - - $alg->getFlowVertex($graph->getVertex(1)); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/GroupsTest.php b/tests/Fhaculty/Graph/Algorithm/GroupsTest.php deleted file mode 100644 index fc01df62..00000000 --- a/tests/Fhaculty/Graph/Algorithm/GroupsTest.php +++ /dev/null @@ -1,62 +0,0 @@ -assertEquals(array(), $alg->getGroups()); - $this->assertEquals(0, $alg->getNumberOfGroups()); - - $this->assertTrue($alg->getVerticesGroup(123)->isEmpty()); - - $this->assertFalse($alg->isBipartit()); - } - - public function testGraphPairIsBipartit() - { - // 1 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setGroup(1); - $v2 = $graph->createVertex(2)->setGroup(2); - $v1->createEdgeTo($v2); - - $alg = new AlgorithmGroups($graph); - - $this->assertEquals(array(1, 2), $alg->getGroups()); - $this->assertEquals(2, $alg->getNumberOfGroups()); - - $this->assertTrue($alg->getVerticesGroup(123)->isEmpty()); - $this->assertEquals(array(1 => $v1), $alg->getVerticesGroup(1)->getMap()); - - $this->assertTrue($alg->isBipartit()); - } - - public function testGraphTriangleCycleIsNotBipartit() - { - // 1 -> 2 -> 3 -> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setGroup(1); - $v2 = $graph->createVertex(2)->setGroup(2); - $v3 = $graph->createVertex(3)->setGroup(1); - $v1->createEdgeTo($v2); - $v2->createEdgeTo($v3); - $v3->createEdgeTo($v1); - - $alg = new AlgorithmGroups($graph); - - $this->assertEquals(array(1, 2), $alg->getGroups()); - $this->assertEquals(2, $alg->getNumberOfGroups()); - - $this->assertTrue($alg->getVerticesGroup(123)->isEmpty()); - $this->assertEquals(array(1 => $v1, 3 => $v3), $alg->getVerticesGroup(1)->getMap()); - - $this->assertFalse($alg->isBipartit()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/LoopTest.php b/tests/Fhaculty/Graph/Algorithm/LoopTest.php deleted file mode 100644 index 2bf206d3..00000000 --- a/tests/Fhaculty/Graph/Algorithm/LoopTest.php +++ /dev/null @@ -1,57 +0,0 @@ -assertFalse($alg->hasLoop()); - } - - public function testGraphWithMixedCircuitIsNotConsideredLoop() - { - // 1 -> 2 - // 2 -- 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v1->createEdgeTo($v2); - $v2->createEdge($v1); - - $alg = new AlgorithmLoop($graph); - - $this->assertFalse($alg->hasLoop()); - $this->assertFalse($alg->hasLoopVertex($v1)); - $this->assertFalse($alg->hasLoopVertex($v2)); - } - - public function testGraphUndirectedLoop() - { - // 1 -- 1 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($v1 = $graph->getVertex(1)); - - $alg = new AlgorithmLoop($graph); - - $this->assertTrue($alg->hasLoop()); - $this->assertTrue($alg->hasLoopVertex($v1)); - } - - public function testGraphDirectedLoop() - { - // 1 -> 1 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($v1 = $graph->getVertex(1)); - - $alg = new AlgorithmLoop($graph); - - $this->assertTrue($alg->hasLoop()); - $this->assertTrue($alg->hasLoopVertex($v1)); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarpTest.php b/tests/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarpTest.php deleted file mode 100644 index bf1e0887..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MaxFlow/EdmondsKarpTest.php +++ /dev/null @@ -1,169 +0,0 @@ - 1 - $graph = new Graph(); - $v0 = $graph->createVertex(0); - $v1 = $graph->createVertex(1); - - $v0->createEdgeTo($v1)->setCapacity(10); - - // 0 -[10/10]-> 1 - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1); - - $this->assertEquals(10, $alg->getFlowMax()); - } - - public function testEdgesMultiplePaths() - { - // 0 -[0/5]---------> 1 - // | ^ - // | | - // \-[0/7]-> 2 -[0/9]-/ - $graph = new Graph(); - $v0 = $graph->createVertex(0); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - - $v0->createEdgeTo($v1)->setCapacity(5); - $v0->createEdgeTo($v2)->setCapacity(7); - $v2->createEdgeTo($v1)->setCapacity(9); - - // 0 -[5/5]---------> 1 - // | ^ - // | | - // \-[7/7]-> 2 -[7/9]-/ - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1); - - $this->assertEquals(12, $alg->getFlowMax()); - } - - public function testEdgesMultiplePathsTwo() - { - // 0 -[0/5]---------> 1-[0/10]-> 3 - // | ^ | - // | | | - // \-[0/7]-> 2 -[0/9]-/ | - // ^ | - // \---[0/2]-----------/ - $graph = new Graph(); - $v0 = $graph->createVertex(0); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $v5 = $graph->createVertex(5); - - $v0->createEdgeTo($v1)->setCapacity(5); - $v0->createEdgeTo($v2)->setCapacity(7); - $v2->createEdgeTo($v1)->setCapacity(9); - $v1->createEdgeTo($v3)->setCapacity(10); - $v3->createEdgeTo($v2)->setCapacity(2); - - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v3); - - $this->assertEquals(10, $alg->getFlowMax()); - - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v2); - - $this->assertEquals(9, $alg->getFlowMax()); - } - - public function testEdgesMultiplePathsTree() - { - $graph = new Graph(); - $v0 = $graph->createVertex(0); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - - $v0->createEdgeTo($v1)->setCapacity(4); - $v0->createEdgeTo($v2)->setCapacity(2); - $v1->createEdgeTo($v2)->setCapacity(3); - $v1->createEdgeTo($v3)->setCapacity(1); - $v2->createEdgeTo($v3)->setCapacity(6); - - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v3); - - $this->assertEquals(6, $alg->getFlowMax()); - } - -// public function testEdgesParallel(){ -// $graph = new Graph(); -// $v0 = $graph->createVertex(0); -// $v1 = $graph->createVertex(1); - -// $v0->createEdgeTo($v1)->setCapacity(3.4); -// $v0->createEdgeTo($v1)->setCapacity(6.6); - -// $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1); - -// $this->assertEquals(10, $alg->getFlowMax()); -// } - - /** - * @expectedException UnexpectedValueException - */ - public function testEdgesUndirected() - { - // 0 -[0/7]- 1 - $graph = new Graph(); - $v0 = $graph->createVertex(0); - $v1 = $graph->createVertex(1); - - $v1->createEdge($v0)->setCapacity(7); - - // 0 -[7/7]- 1 - $alg = new AlgorithmMaxFlowEdmondsKarp($v0, $v1); - - $this->assertEquals(7, $alg->getFlowMax()); - } - - /** - * run algorithm with bigger graph and check result against known result (will take several seconds) - */ -// public function testKnownResultBig(){ - -// $graph = $this->readGraph('G_1_2.txt'); - -// $alg = new AlgorithmMaxFlowEdmondsKarp($graph->getVertex(0), $graph->getVertex(4)); - -// $this->assertEquals(0.735802, $alg->getFlowMax()); -// } - - - /** - * @expectedException InvalidArgumentException - */ - public function testInvalidFlowToOtherGraph() - { - $graph1 = new Graph(); - $vg1 = $graph1->createVertex(1); - - $graph2 = new Graph(); - $vg2 = $graph2->createVertex(2); - - new AlgorithmMaxFlowEdmondsKarp($vg1, $vg2); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testInvalidFlowToSelf() - { - $graph = new Graph(); - $v1 = $graph->createVertex(1); - - new AlgorithmMaxFlowEdmondsKarp($v1, $v1); - } - -} diff --git a/tests/Fhaculty/Graph/Algorithm/MaximumMatching/FlowTest.php b/tests/Fhaculty/Graph/Algorithm/MaximumMatching/FlowTest.php deleted file mode 100644 index b94a9914..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MaximumMatching/FlowTest.php +++ /dev/null @@ -1,65 +0,0 @@ -setEnableDirectedEdges(false); -// $graph = $loader->createGraph(); - -// $alg = new Flow($graph); -// $this->assertEquals(100, $alg->getNumberOfMatches()); -// } - - public function testSingleEdge() - { - $graph = new Graph(); - $edge = $graph->createVertex(0)->setGroup(0)->createEdge($graph->createVertex(1)->setGroup(1)); - - $alg = new Flow($graph); - // correct number of edges - $this->assertEquals(1, $alg->getNumberOfMatches()); - // actual edge instance returned - $this->assertEquals(array($edge), $alg->getEdges()->getVector()); - - // check - $flowgraph = $alg->createGraph(); - $this->assertInstanceOf('Fhaculty\Graph\Graph', $flowgraph); - } - - /** - * expect exception for directed edges - * @expectedException UnexpectedValueException - */ - public function testInvalidDirected() - { - $graph = new Graph(); - $graph->createVertex(0)->setGroup(0)->createEdgeTo($graph->createVertex(1)->setGroup(1)); - - $alg = new Flow($graph); - $alg->getNumberOfMatches(); - } - - /** - * expect exception for non-bipartit graphs - * @expectedException UnexpectedValueException - */ - public function testInvalidBipartit() - { - $graph = new Graph(); - $graph->createVertex(0)->setGroup(1)->createEdge($graph->createVertex(1)->setGroup(1)); - - $alg = new Flow($graph); - $alg->getNumberOfMatches(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/BaseMcfTest.php b/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/BaseMcfTest.php deleted file mode 100644 index 95e87f16..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/BaseMcfTest.php +++ /dev/null @@ -1,203 +0,0 @@ -createAlgorithm($graph); - $this->assertEquals(0, $alg->getWeightFlow()); - } - - public function testSingleIntermediary() - { - $graph = new Graph(); - $v1 = $graph->createVertex(1); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(0, $alg->getWeightFlow()); - } - - public function testSimpleEdge() - { - // 1(+2) -[0/2/2]-> 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - $v1->createEdgeTo($v2)->setWeight(2)->setCapacity(2); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(4, $alg->getWeightFlow()); // 2x2 - } - - public function testMultipleSinks() - { - // 1(+2) -[0/2/2]-> 2(-1) - // -[0/4/-5]-> 3(-1) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-1); - $v3 = $graph->createVertex(3)->setBalance(-1); - $v1->createEdgeTo($v2)->setWeight(2)->setCapacity(2); - $v1->createEdgeTo($v3)->setWeight(-5)->setCapacity(4); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(-3, $alg->getWeightFlow()); // 1*2 + 1*-5 - } - - public function testIntermediaryVertices() - { - // 1(+2) -[0/1/4]-> 2 -[0/6/-2]-> 4(-2) - // -[0/4/5]-> 3 -[0/6/8]-> - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4)->setBalance(-2); - $v1->createEdgeTo($v2)->setWeight(4)->setCapacity(1); - $v2->createEdgeTo($v4)->setWeight(-2)->setCapacity(6); - $v1->createEdgeTo($v3)->setWeight(5)->setCapacity(4); - $v3->createEdgeTo($v4)->setWeight(8)->setCapacity(6); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(15, $alg->getWeightFlow()); // 1*4 + 1*-2 + 1*5 + 1*8 - } - - public function testEdgeCapacities() - { - // 1(+2) -[0/3/4]-> 2 -[0/4/5]-> 3 ->[0/6/-2]-> 4(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4)->setBalance(-2); - $v1->createEdgeTo($v2)->setWeight(4)->setCapacity(3); - $v2->createEdgeTo($v3)->setWeight(5)->setCapacity(4); - $v3->createEdgeTo($v4)->setWeight(-2)->setCapacity(6); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(14, $alg->getWeightFlow()); // 2*4 + 2*5 + 2*-2 - } - - public function testEdgeFlows() - { - // 1(+4) ---[3/4/2]---> 2 ---[3/3/3]---> 4(-4) - // | | ^ - // | [0/2/1] | - // | ↓ | - // \-------[1/2/2]---> 3 ---[1/5/1]-------/ - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(4); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4)->setBalance(-4); - $v1->createEdgeTo($v2)->setFlow(3)->setCapacity(4)->setWeight(2); - $v2->createEdgeTo($v4)->setFlow(3)->setCapacity(3)->setWeight(3); - $v1->createEdgeTo($v3)->setFlow(1)->setCapacity(2)->setWeight(2); - $v3->createEdgeTo($v4)->setFlow(1)->setCapacity(5)->setWeight(1); - $v2->createEdgeTo($v3)->setFlow(0)->setCapacity(2)->setWeight(1); - - $alg = $this->createAlgorithm($graph); - $this->assertEquals(14, $alg->getWeightFlow()); // 4*1 + 2*2 + 2*1 + 2*2 - } - - /** - * @expectedException UnexpectedValueException - */ - public function testEdgeCapacityInsufficientFails() - { - // 1(+2) -[0/1]-> 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - $v1->createEdgeTo($v2)->setCapacity(1); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testEdgeCapacityUnsetFails() - { - // 1(+2) -> 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - $v1->createEdgeTo($v2); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testIsolatedVerticesFail() - { - // 1(+2), 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testUnbalancedFails() - { - // 1(+2) -> 2(-3) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-3); - $v1->createEdgeTo($v2)->setCapacity(3); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testUndirectedFails() - { - // 1(+2) -- 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - $v1->createEdge($v2)->setCapacity(2); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testUndirectedNegativeCycleFails() - { - // 1(+2) -[0/2/-1]- 2(-2) - $graph = new Graph(); - $v1 = $graph->createVertex(1)->setBalance(2); - $v2 = $graph->createVertex(2)->setBalance(-2); - $v1->createEdge($v2)->setCapacity(2)->setWeight(-1); - - $alg = $this->createAlgorithm($graph); - $alg->getWeightFlow(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCancellingTest.php b/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCancellingTest.php deleted file mode 100644 index 6ae2527f..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCancellingTest.php +++ /dev/null @@ -1,12 +0,0 @@ -createVertex(1); - - $alg = $this->createAlg($v1); - - $this->assertCount(0, $alg->getEdges()); - $this->assertEquals(0, $alg->getWeight()); - - $graphMst = $alg->createGraph(); - $this->assertGraphEquals($graph, $graphMst); - } - - public function testSingleEdge() - { - // 1 --[3]-- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v1->createEdge($v2)->setWeight(3); - - $alg = $this->createAlg($v1); - - $this->assertCount(1, $alg->getEdges()); - $this->assertEquals(3, $alg->getWeight()); - $this->assertGraphEquals($graph, $alg->createGraph()); - } - - public function testSimpleGraph() - { - // 1 --[6]-- 2 --[9]-- 3 --[7]-- 4 --[8]-- 5 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $v5 = $graph->createVertex(5); - $v1->createEdge($v2)->setWeight(6); - $v2->createEdge($v3)->setWeight(9); - $v3->createEdge($v4)->setWeight(7); - $v4->createEdge($v5)->setWeight(8); - - $alg = $this->createAlg($v1); - - $graphMst = $alg->createGraph(); - $this->assertGraphEquals($graph, $graphMst); - } - - public function testFindingCheapestEdge() - { - // /--[4]--\ - // / \ - // 1 ---[3]--- 2 - // \ / - // \--[5]--/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v1->createEdge($v2)->setWeight(4); - $v1->createEdge($v2)->setWeight(3); - $v1->createEdge($v2)->setWeight(5); - - $alg = $this->createAlg($v1); - $edges = $alg->getEdges(); - - $this->assertCount(1, $edges); - $this->assertEquals(3, $edges->getEdgeFirst()->getWeight()); - $this->assertEquals(3, $alg->getWeight()); - } - - public function testFindingCheapestTree() - { - // 1 --[4]-- 2 --[5]-- 3 - // \ / - // \-------[6]-----/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v1->createEdge($v2)->setWeight(4); - $v2->createEdge($v3)->setWeight(5); - $v3->createEdge($v1)->setWeight(6); - - // 1 --[4]-- 2 -- [5] -- 3 - $graphExpected = new Graph(); - $ve1 = $graphExpected->createVertex(1); - $ve2 = $graphExpected->createVertex(2); - $ve3 = $graphExpected->createVertex(3); - $ve1->createEdge($ve2)->setWeight(4); - $ve2->createEdge($ve3)->setWeight(5); - - $alg = $this->createAlg($v1); - $this->assertCount(2, $alg->getEdges()); - $this->assertEquals(9, $alg->getWeight()); - $this->assertGraphEquals($graphExpected, $alg->createGraph()); - } - - public function testMixedGraphDirectionIsIgnored() - { - // 1 --[6]-> 2 --[7]-- 3 --[8]-- 4 <-[9]-- 5 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $v5 = $graph->createVertex(5); - $v1->createEdgeTo($v2)->setWeight(6); - $v2->createEdge($v3)->setWeight(7); - $v4->createEdge($v3)->setWeight(8); - $v5->createEdgeTo($v4)->setWeight(9); - - $alg = $this->createAlg($v1); - - $this->assertCount(4, $alg->getEdges()); - $this->assertEquals(30, $alg->getWeight()); - $this->assertGraphEquals($graph, $alg->createGraph()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testMultipleComponentsFail() - { - // 1 --[1]-- 2, 3 --[1]-- 4 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $v1->createEdge($v2)->setWeight(1); - $v3->createEdge($v4)->setWeight(1); - - $alg = $this->createAlg($v1); - $alg->getEdges(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testMultipleIsolatedVerticesFormMultipleComponentsFail() - { - // 1, 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - - $alg = $this->createAlg($v1); - $alg->getEdges(); - } - - -} \ No newline at end of file diff --git a/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/KruskalTest.php b/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/KruskalTest.php deleted file mode 100644 index 20b31f76..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/KruskalTest.php +++ /dev/null @@ -1,24 +0,0 @@ -getGraph()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testNullGraphIsNotConsideredToBeConnected() - { - $graph = new Graph(); - - $alg = new Kruskal($graph); - $alg->getEdges(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/PrimTest.php b/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/PrimTest.php deleted file mode 100644 index c69bb6bf..00000000 --- a/tests/Fhaculty/Graph/Algorithm/MinimumSpanningTree/PrimTest.php +++ /dev/null @@ -1,12 +0,0 @@ -assertFalse($alg->hasEdgeParallel()); - } - - public function testDirectedCycleIsNotConsideredParallel() - { - // 1 -> 2 - // 2 -> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v2->createEdgeTo($v1); - - $alg = new AlgorithmParallel($graph); - - $this->assertFalse($alg->hasEdgeParallel()); - $this->assertEquals(array(), $alg->getEdgesParallelEdge($e1)->getVector()); - $this->assertEquals(array(), $alg->getEdgesParallelEdge($e2)->getVector()); - } - - public function testDirectedParallelEdge() - { - // 1 -> 2 - // 1 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v1->createEdgeTo($v2); - - $alg = new AlgorithmParallel($graph); - - $this->assertTrue($alg->hasEdgeParallel()); - $this->assertEquals(array($e2), $alg->getEdgesParallelEdge($e1)->getVector()); - $this->assertEquals(array($e1), $alg->getEdgesParallelEdge($e2)->getVector()); - } - - public function testMixedParallelEdge() - { - // 1 -> 2 - // 1 -- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v1->createEdge($v2); - - $alg = new AlgorithmParallel($graph); - - $this->assertTrue($alg->hasEdgeParallel()); - $this->assertEquals(array($e2), $alg->getEdgesParallelEdge($e1)->getVector()); - $this->assertEquals(array($e1), $alg->getEdgesParallelEdge($e2)->getVector()); - } - - public function testMixedParallelEdgesMultiple() - { - // 1 -> 2 - // 1 -> 2 - // 1 -- 2 - // 1 -- 2 - // 2 -> 1 - // 2 -> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v1->createEdgeTo($v2); - $e3 = $v1->createEdge($v2); - $e4 = $v1->createEdge($v2); - $e5 = $v2->createEdgeTo($v1); - $e6 = $v2->createEdgeTo($v1); - - $alg = new AlgorithmParallel($graph); - - $this->assertTrue($alg->hasEdgeParallel()); - $this->assertEquals(array($e2, $e3, $e4), $alg->getEdgesParallelEdge($e1)->getVector()); - $this->assertEquals(array($e1, $e3, $e4), $alg->getEdgesParallelEdge($e2)->getVector()); - $this->assertEquals(array($e1, $e2, $e4, $e5, $e6), $alg->getEdgesParallelEdge($e3)->getVector()); - $this->assertEquals(array($e1, $e2, $e3, $e5, $e6), $alg->getEdgesParallelEdge($e4)->getVector()); - $this->assertEquals(array($e3, $e4, $e6), $alg->getEdgesParallelEdge($e5)->getVector()); - $this->assertEquals(array($e3, $e4, $e5), $alg->getEdgesParallelEdge($e6)->getVector()); - } - -} diff --git a/tests/Fhaculty/Graph/Algorithm/Property/PropertyGraphTest.php b/tests/Fhaculty/Graph/Algorithm/Property/PropertyGraphTest.php deleted file mode 100644 index 16136bbf..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Property/PropertyGraphTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertTrue($alg->isNull()); - $this->assertTrue($alg->isEdgeless()); - $this->assertFalse($alg->isTrivial()); - } - - public function testSingleVertexIsTrivial() - { - $graph = new Graph(); - $graph->createVertex(1); - - $alg = new GraphProperty($graph); - - $this->assertFalse($alg->isNull()); - $this->assertTrue($alg->isEdgeless()); - $this->assertTrue($alg->isTrivial()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/Property/WalkPropertyTest.php b/tests/Fhaculty/Graph/Algorithm/Property/WalkPropertyTest.php deleted file mode 100644 index 8fd4313b..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Property/WalkPropertyTest.php +++ /dev/null @@ -1,186 +0,0 @@ -createVertex(1); - - $walk = Walk::factoryFromEdges(array(), $v1); - - $this->assertEquals(1, count($walk->getVertices())); - $this->assertEquals(0, count($walk->getEdges())); - - $alg = new WalkProperty($walk); - - $this->assertFalse($alg->isLoop()); - $this->assertFalse($alg->hasLoop()); - - $this->assertFalse($alg->isCycle()); - $this->assertFalse($alg->hasCycle()); - - $this->assertTrue($alg->isPath()); - $this->assertTrue($alg->isSimple()); - - $this->assertTrue($alg->isEulerian()); - $this->assertTrue($alg->isHamiltonian()); - } - - public function testLoop() - { - // 1 -- 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $e1 = $v1->createEdge($v1); - - $walk = Walk::factoryFromEdges(array($e1), $v1); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isLoop()); - $this->assertTrue($alg->hasLoop()); - - $this->assertTrue($alg->isCycle()); - $this->assertTrue($alg->hasCycle()); - - $this->assertTrue($alg->isPath()); - $this->assertTrue($alg->isSimple()); - - $this->assertTrue($alg->isEulerian()); - $this->assertTrue($alg->isHamiltonian()); - } - - public function testCycle() - { - // 1 -- 2 -- 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdge($v2); - $e2 = $v2->createEdge($v1); - - $walk = Walk::factoryFromEdges(array($e1, $e2), $v1); - - $this->assertEquals(3, count($walk->getVertices())); - $this->assertEquals(2, count($walk->getEdges())); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isCycle()); - $this->assertTrue($alg->hasCycle()); - $this->assertTrue($alg->isPath()); - $this->assertTrue($alg->isSimple()); - - $this->assertTrue($alg->isEulerian()); - $this->assertTrue($alg->isHamiltonian()); - } - - public function testCircuit() - { - // 1 -> 2 -> 1, 2 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v2->createEdgeTo($v1); - $e3 = $v2->createEdgeTo($v2); - - // 1 -> 2 -> 2 -> 1 - $walk = Walk::factoryFromEdges(array($e1, $e3, $e2), $v1); - - $this->assertEquals(array(1, 2, 2, 1), $walk->getVertices()->getIds()); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isCycle()); - $this->assertTrue($alg->isCircuit()); - } - - public function testNonCircuit() - { - // 1 -> 2 -> 1, 2 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v2->createEdgeTo($v1); - $e3 = $v2->createEdgeTo($v2); - - // non-circuit: taking loop twice - // 1 -> 2 -> 2 -> 2 -> 1 - $walk = Walk::factoryFromEdges(array($e1, $e3, $e3, $e2), $v1); - - $this->assertEquals(array(1, 2, 2, 2, 1), $walk->getVertices()->getIds()); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isCycle()); - $this->assertFalse($alg->isCircuit()); - } - - public function testDigon() - { - // 1 -> 2 -> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v2->createEdgeTo($v1); - - $walk = Walk::factoryFromEdges(array($e1, $e2), $v1); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isDigon()); - } - - public function testTriangle() - { - // 1 -> 2 -> 3 -> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $e1 = $v1->createEdgeTo($v2); - $e2 = $v2->createEdgeTo($v3); - $e3 = $v3->createEdgeTo($v1); - - $walk = Walk::factoryFromEdges(array($e1, $e2, $e3), $v1); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isTriangle()); - } - - public function testSimplePathWithinGraph() - { - // 1 -- 2 -- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdge($v2); - $e2 = $v2->createEdge($v2); - - // only use "2 -- 2" part - $walk = Walk::factoryFromEdges(array($e2), $v2); - - $this->assertEquals(2, count($walk->getVertices())); - $this->assertEquals(1, count($walk->getEdges())); - - $alg = new WalkProperty($walk); - - $this->assertTrue($alg->isCycle()); - $this->assertTrue($alg->hasCycle()); - $this->assertTrue($alg->isPath()); - $this->assertTrue($alg->isSimple()); - - $this->assertFalse($alg->isEulerian()); - $this->assertFalse($alg->isHamiltonian()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/ResidualGraphTest.php b/tests/Fhaculty/Graph/Algorithm/ResidualGraphTest.php deleted file mode 100644 index d173c13f..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ResidualGraphTest.php +++ /dev/null @@ -1,133 +0,0 @@ -createGraph(); - - $this->assertGraphEquals($graph, $residual); - } - - /** - * test an edge with capacity unused - */ - public function testEdgeUnused() - { - $graph = new Graph(); - - $graph->createVertex(0)->createEdgeTo($graph->createVertex(1))->setFlow(0) - ->setCapacity(2) - ->setWeight(3); - - $alg = new ResidualGraph($graph); - $residual = $alg->createGraph(); - - $this->assertGraphEquals($graph, $residual); - } - - /** - * test an edge with capacity completely used - */ - public function testEdgeUsed() - { - $graph = new Graph(); - - $graph->createVertex(0)->createEdgeTo($graph->createVertex(1))->setFlow(2) - ->setCapacity(2) - ->setWeight(3); - - $alg = new ResidualGraph($graph); - $residual = $alg->createGraph(); - - $expected = new Graph(); - $expected->createVertex(1)->createEdgeTo($expected->createVertex(0))->setFlow(0) - ->setCapacity(2) - ->setWeight(-3); - - $this->assertGraphEquals($expected, $residual); - } - - /** - * test an edge with capacity remaining - */ - public function testEdgePartial() - { - $graph = new Graph(); - - $graph->createVertex(0)->createEdgeTo($graph->createVertex(1))->setFlow(1) - ->setCapacity(2) - ->setWeight(3); - - $alg = new ResidualGraph($graph); - $residual = $alg->createGraph(); - - $expected = new Graph(); - $expected->createVertex(0); - $expected->createVertex(1); - - // remaining edge - $expected->getVertex(0)->createEdgeTo($expected->getVertex(1))->setFlow(0) - ->setCapacity(1) - ->setWeight(3); - - // back edge - $expected->getVertex(1)->createEdgeTo($expected->getVertex(0))->setFlow(0) - ->setCapacity(1) - ->setWeight(-3); - - $this->assertGraphEquals($expected, $residual); - } - - /** - * expect exception for undirected edges - * @expectedException UnexpectedValueException - */ - public function testInvalidUndirected() - { - $graph = new Graph(); - - $graph->createVertex()->createEdge($graph->createVertex())->setFlow(1) - ->setCapacity(2); - - $alg = new ResidualGraph($graph); - $alg->createGraph(); - } - - /** - * expect exception for edges with no flow - * @expectedException UnexpectedValueException - */ - public function testInvalidNoFlow() - { - $graph = new Graph(); - - $graph->createVertex()->createEdgeTo($graph->createVertex())->setCapacity(1); - - $alg = new ResidualGraph($graph); - $alg->createGraph(); - } - - /** - * expect exception for edges with no capacity - * @expectedException UnexpectedValueException - */ - public function testInvalidNoCapacity() - { - $graph = new Graph(); - - $graph->createVertex()->createEdgeTo($graph->createVertex())->setFlow(1); - - $alg = new ResidualGraph($graph); - $alg->createGraph(); - } - -} diff --git a/tests/Fhaculty/Graph/Algorithm/ShortestPath/BaseShortestPathTest.php b/tests/Fhaculty/Graph/Algorithm/ShortestPath/BaseShortestPathTest.php deleted file mode 100644 index 7377e8ec..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ShortestPath/BaseShortestPathTest.php +++ /dev/null @@ -1,169 +0,0 @@ -createVertex(1); - - $alg = $this->createAlg($v1); - - $this->assertFalse($alg->hasVertex($v1)); - //$this->assertEquals(0, $alg->getDistance($v1)); - $this->assertEquals(array(), $alg->getDistanceMap()); - $this->assertEquals(array(), $alg->getEdges()->getVector()); - //$this->assertEquals(array(), $alg->getEdgesTo($v1)); - $this->assertEquals(array(), $alg->getVertices()->getVector()); - $this->assertEquals(array(), $alg->getVertices()->getIds()); - - $clone = $alg->createGraph(); - $this->assertGraphEquals($graph,$clone); - } - - public function testGraphSingleLoop() - { - // 1 -[4]> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $e1 = $v1->createEdgeTo($v1)->setWeight(4); - - $alg = $this->createAlg($v1); - - $this->assertEquals(array($e1), $alg->getEdges()->getVector()); - - $expectedWeight = $this->getExpectedWeight(array($e1)); - $this->assertTrue($alg->hasVertex($v1)); - $this->assertEquals($expectedWeight, $alg->getDistance($v1)); - $this->assertEquals(array(1 => $expectedWeight), $alg->getDistanceMap()); - $this->assertEquals(array($e1), $alg->getEdgesTo($v1)->getVector()); - $this->assertEquals(array(1 => $v1), $alg->getVertices()->getMap()); - $this->assertEquals(array(1), $alg->getVertices()->getIds()); - } - - public function testGraphCycle() - { - // 1 -[4]-> 2 -[2]-> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2)->setWeight(4); - $e2 = $v2->createEdgeTo($v1)->setWeight(2); - - $alg = $this->createAlg($v1); - - //$this->assertEquals(array($e2, $e1), $alg->getEdges()); - - $expectedWeight = $this->getExpectedWeight(array($e1)); - $this->assertTrue($alg->hasVertex($v2)); - $this->assertEquals(array($e1), $alg->getEdgesTo($v2)->getVector()); - $this->assertEquals($expectedWeight, $alg->getDistance($v2)); - - $expectedWeight = $this->getExpectedWeight(array($e1, $e2)); - $this->assertTrue($alg->hasVertex($v1)); - $this->assertEquals(array($e1, $e2), $alg->getEdgesTo($v1)->getVector()); - $this->assertEquals($expectedWeight, $alg->getDistance($v1)); - - $walk = $alg->getWalkTo($v1); - $this->assertEquals(2, count($walk->getEdges())); - } - - /** - * @expectedException OutOfBoundsException - */ - public function testIsolatedVertexIsNotReachable() - { - // 1, 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - - $alg = $this->createAlg($v1); - - $this->assertFalse($alg->hasVertex($v2)); - - $alg->getEdgesTo($v2); - } - - /** - * @expectedException OutOfBoundsException - */ - public function testSeparateGraphsAreNotReachable() - { - // 1 - $graph1 = new Graph(); - $vg1 = $graph1->createVertex(1); - - $graph2 = new Graph(); - $vg2 = $graph2->createVertex(1); - - $alg = $this->createAlg($vg1); - - $alg->getEdgesTo($vg2); - } - - public function testGraphUnweighted() - { - // 1 -> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2); - - $alg = $this->createAlg($v1); - - $expectedWeight = $this->getExpectedWeight(array($e1)); - $this->assertEquals($expectedWeight, $alg->getDistance($v2)); - $this->assertEquals(array(2 => $expectedWeight), $alg->getDistanceMap()); - $this->assertEquals(array($e1), $alg->getEdges()->getVector()); - $this->assertEquals(array($e1), $alg->getEdgesTo($v2)->getVector()); - $this->assertEquals(array(2), $alg->getVertices()->getIds()); - } - - public function testGraphTwoComponents() - { - // 1 -[10]-> 2 - // 3 -[20]-> 4 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $e1 = $v1->createEdgeTo($v2)->setWeight(10); - $e2 = $v3->createEdgeTo($v4)->setWeight(20); - - $alg = $this->createAlg($v1); - - $expectedWeight = $this->getExpectedWeight(array($e1)); - $this->assertEquals($expectedWeight, $alg->getDistance($v2)); - $this->assertEquals(array(2 => $expectedWeight), $alg->getDistanceMap()); - $this->assertEquals(array($e1), $alg->getEdges()->getVector()); - // $this->assertEquals(array(), $alg->getEdgesTo($v1)); - $this->assertEquals(array($e1), $alg->getEdgesTo($v2)->getVector()); - $this->assertEquals(array(2 => $v2), $alg->getVertices()->getMap()); - $this->assertEquals(array(2), $alg->getVertices()->getIds()); - } - - protected function getExpectedWeight($edges) - { - $sum = 0; - foreach ($edges as $edge) { - $sum += $edge->getWeight(); - } - return $sum; - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirstTest.php b/tests/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirstTest.php deleted file mode 100644 index e01a2e26..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ShortestPath/BreadthFirstTest.php +++ /dev/null @@ -1,38 +0,0 @@ - 2 - // 1 -[-1]-> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2)->setWeight(10); - $e2 = $v1->createEdgeTo($v2)->setWeight(-1); - - $alg = $this->createAlg($v1); - - $this->assertEquals(1, $alg->getDistance($v2)); - $this->assertEquals(array(2 => 1), $alg->getDistanceMap()); - $this->assertEquals(array($e1), $alg->getEdges()->getVector()); - $this->assertEquals(array($e1), $alg->getEdgesTo($v2)->getVector()); - $this->assertEquals(array(2 => $v2), $alg->getVertices()->getMap()); - $this->assertEquals(array(2), $alg->getVertices()->getIds()); - } - - protected function getExpectedWeight($edges) - { - return count($edges); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/ShortestPath/DijkstraTest.php b/tests/Fhaculty/Graph/Algorithm/ShortestPath/DijkstraTest.php deleted file mode 100644 index b89dea62..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ShortestPath/DijkstraTest.php +++ /dev/null @@ -1,31 +0,0 @@ - 2 - // 1 -[-1]-> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2)->setWeight(10); - $e2 = $v1->createEdgeTo($v2)->setWeight(-1); - - $alg = $this->createAlg($v1); - - $alg->getEdges(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFordTest.php b/tests/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFordTest.php deleted file mode 100644 index 3afefa73..00000000 --- a/tests/Fhaculty/Graph/Algorithm/ShortestPath/MooreBellmanFordTest.php +++ /dev/null @@ -1,96 +0,0 @@ - 2 - // 1 -[-1]-> 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdgeTo($v2)->setWeight(10); - $e2 = $v1->createEdgeTo($v2)->setWeight(-1); - - $alg = $this->createAlg($v1); - - // $this->assertEquals(0, $alg->getDistance($v1)); - $this->assertEquals(-1, $alg->getDistance($v2)); - $this->assertEquals(array(2 => -1), $alg->getDistanceMap()); - $this->assertEquals(array($e2), $alg->getEdges()->getVector()); - //$this->assertEquals(array(), $alg->getEdgesTo($v1)); - $this->assertEquals(array($e2), $alg->getEdgesTo($v2)->getVector()); - $this->assertEquals(array(2 => $v2), $alg->getVertices()->getMap()); - $this->assertEquals(array(2), $alg->getVertices()->getIds()); - - return $alg; - } - - /** - * @param MooreBellmanFord $alg - * @depends testGraphParallelNegative - * @expectedException UnderflowException - */ - public function testNoNegativeCycle(MooreBellmanFord $alg) - { - $alg->getCycleNegative(); - } - - public function testUndirectedNegativeWeightIsCycle() - { - // 1 -[-10]- 2 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $e1 = $v1->createEdge($v2)->setWeight(-10); - - $alg = $this->createAlg($v1); - - $cycle = $alg->getCycleNegative(); - } - - public function testLoopNegativeWeightIsCycle() - { - // 1 -[-10]-> 1 - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $e1 = $v1->createEdge($v1)->setWeight(-10); - - $alg = $this->createAlg($v1); - - $cycle = $alg->getCycleNegative(); - } - - public function testNegativeComponentHasCycle() - { - // 1 -[1]-> 2 3 --[-1]--> 4 - // ^ | - // \---[-2]----/ - $graph = new Graph(); - $v1 = $graph->createVertex(1); - $v2 = $graph->createVertex(2); - $v3 = $graph->createVertex(3); - $v4 = $graph->createVertex(4); - $e1 = $v1->createEdgeTo($v2)->setWeight(1); - $e2 = $v3->createEdgeTo($v4)->setWeight(-1); - $e3 = $v4->createEdgeTo($v3)->setWeight(-2); - - // second component has a cycle - $alg = $this->createAlg($v3); - $cycle = $alg->getCycleNegative(); - - // first component does not have a cycle - $alg = $this->createAlg($v1); - $this->setExpectedException('UnderflowException'); - $alg->getCycleNegative(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/SymmetricTest.php b/tests/Fhaculty/Graph/Algorithm/SymmetricTest.php deleted file mode 100644 index cd689aaf..00000000 --- a/tests/Fhaculty/Graph/Algorithm/SymmetricTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertTrue($alg->isSymmetric()); - } - - public function testGraphIsolated() - { - $graph = new Graph(); - $graph->createVertex(1); - $graph->createVertex(2); - - $alg = new AlgorithmSymmetric($graph); - - $this->assertTrue($alg->isSymmetric()); - } - - public function testGraphSingleArcIsNotSymmetricr() - { - // 1 -> 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - - $alg = new AlgorithmSymmetric($graph); - - $this->assertFalse($alg->isSymmetric()); - } - - public function testGraphAntiparallelIsSymmetricr() - { - // 1 -> 2 -> 1 - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - $graph->getVertex(2)->createEdgeTo($graph->getVertex(1)); - - $alg = new AlgorithmSymmetric($graph); - - $this->assertTrue($alg->isSymmetric()); - } - - public function testGraphSingleUndirectedIsSymmetricr() - { - // 1 -- 2 - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - - $alg = new AlgorithmSymmetric($graph); - - $this->assertTrue($alg->isSymmetric()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/TopologicalSortTest.php b/tests/Fhaculty/Graph/Algorithm/TopologicalSortTest.php deleted file mode 100644 index 01a16873..00000000 --- a/tests/Fhaculty/Graph/Algorithm/TopologicalSortTest.php +++ /dev/null @@ -1,77 +0,0 @@ -assertInstanceOf('Fhaculty\Graph\Set\Vertices', $alg->getVertices()); - $this->assertTrue($alg->getVertices()->isEmpty()); - } - - public function testGraphIsolated() - { - $graph = new Graph(); - $graph->createVertex(1); - $graph->createVertex(2); - - $alg = new TopologicalSort($graph); - - $this->assertSame(array($graph->getVertex(1), $graph->getVertex(2)), $alg->getVertices()->getVector()); - } - - public function testGraphSimple() - { - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - - $alg = new TopologicalSort($graph); - - $this->assertSame(array($graph->getVertex(1), $graph->getVertex(2)), $alg->getVertices()->getVector()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testFailUndirected() - { - $graph = new Graph(); - $graph->createVertex(1)->createEdge($graph->createVertex(2)); - - $alg = new TopologicalSort($graph); - $alg->getVertices(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testFailLoop() - { - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->getVertex(1)); - - $alg = new TopologicalSort($graph); - $alg->getVertices(); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testFailCycle() - { - $graph = new Graph(); - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2)); - $graph->getVertex(2)->createEdgeTo($graph->getVertex(1)); - - $alg = new TopologicalSort($graph); - $alg->getVertices(); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/Tree/BaseDirectedTest.php b/tests/Fhaculty/Graph/Algorithm/Tree/BaseDirectedTest.php deleted file mode 100644 index 0ed3cb52..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Tree/BaseDirectedTest.php +++ /dev/null @@ -1,199 +0,0 @@ -createTreeAlg($graph); - $this->assertFalse($tree->isTree()); - $this->assertTrue($tree->getVerticesLeaf()->isEmpty()); - $this->assertTrue($tree->getVerticesInternal()->isEmpty()); - - return $tree; - } - - /** - * @param BaseDirected $tree - * @depends testNullGraph - * @expectedException UnderflowException - */ - public function testEmptyGraphDoesNotHaveRootVertex(BaseDirected $tree) - { - $tree->getVertexRoot(); - } - - /** - * @param BaseDirected $tree - * @depends testNullGraph - * @expectedException UnderflowException - */ - public function testEmptyGraphDoesNotHaveDegree(BaseDirected $tree) - { - $tree->getDegree(); - } - - /** - * @param BaseDirected $tree - * @depends testNullGraph - * @expectedException UnderflowException - */ - public function testEmptyGraphDoesNotHaveHeight(BaseDirected $tree) - { - $tree->getHeight(); - } - - public function testGraphTree() - { - $graph = $this->createGraphTree(); - $root = $graph->getVertices()->getVertexFirst(); - - $nonRoot = $graph->getVertices()->getMap(); - unset($nonRoot[$root->getId()]); - $nonRoot = new Vertices($nonRoot); - - $c1 = $nonRoot->getVertexFirst(); - - $tree = $this->createTreeAlg($graph); - - $this->assertTrue($tree->isTree()); - $this->assertSame($root, $tree->getVertexRoot()); - $this->assertSame($graph->getVertices()->getVector(), $tree->getVerticesSubtree($root)->getVector()); - $this->assertSame($nonRoot->getVector(), $tree->getVerticesChildren($root)->getVector()); - $this->assertSame($nonRoot->getVector(), $tree->getVerticesDescendant($root)->getVector()); - $this->assertSame($nonRoot->getVector(), $tree->getVerticesLeaf()->getVector()); - $this->assertSame(array(), $tree->getVerticesInternal()->getVector()); - $this->assertSame($root, $tree->getVertexParent($c1)); - $this->assertSame(array(), $tree->getVerticesChildren($c1)->getVector()); - $this->assertSame(array(), $tree->getVerticesDescendant($c1)->getVector()); - $this->assertSame(array($c1), $tree->getVerticesSubtree($c1)->getVector()); - $this->assertEquals(2, $tree->getDegree()); - $this->assertEquals(0, $tree->getDepthVertex($root)); - $this->assertEquals(1, $tree->getDepthVertex($c1)); - $this->assertEquals(1, $tree->getHeight()); - $this->assertEquals(1, $tree->getHeightVertex($root)); - $this->assertEquals(0, $tree->getHeightvertex($c1)); - - return $tree; - } - - /** - * - * @param BaseDirected $tree - * @depends testGraphTree - * @expectedException UnderflowException - */ - public function testGraphTreeRootDoesNotHaveParent(BaseDirected $tree) - { - $root = $tree->getVertexRoot(); - $tree->getVertexParent($root); - } - - public function testNonTree() - { - $graph = $this->createGraphNonTree(); - - $tree = $this->createTreeAlg($graph); - - $this->assertFalse($tree->isTree()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testNonTreeVertexHasMoreThanOneParent() - { - $graph = $this->createGraphNonTree(); - - $tree = $this->createTreeAlg($graph); - - $tree->getVertexParent($graph->getVertex('v3')); - } - - public function testGraphWithParallelEdgeIsNotTree() - { - $graph = $this->createGraphParallelEdge(); - - $tree = $this->createTreeAlg($graph); - - $this->assertFalse($tree->isTree()); - } - - public function testGraphWithLoopIsNotTree() - { - // v1 -> v1 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->getVertex('v1')); - - $tree = $this->createTreeAlg($graph); - - $this->assertFalse($tree->isTree()); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testGraphWithLoopCanNotGetSubgraph() - { - // v1 -> v1 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->getVertex('v1')); - - $tree = $this->createTreeAlg($graph); - - $tree->getVerticesSubtree($graph->getVertex('v1')); - } - - public function testGraphWithUndirectedEdgeIsNotTree() - { - // v1 -- v2 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - - $tree = $this->createTreeAlg($graph); - - $this->assertFalse($tree->isTree()); - } - - public function testGraphWithMixedEdgesIsNotTree() - { - // v1 -> v2 -- v3 -> v4 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->createVertex('v2')); - $graph->getVertex('v2')->createEdge($graph->createVertex('v3')); - $graph->getVertex('v3')->createEdgeTo($graph->createVertex('v4')); - - $tree = $this->createTreeAlg($graph); - - $this->assertFalse($tree->isTree()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/Tree/InTreeTest.php b/tests/Fhaculty/Graph/Algorithm/Tree/InTreeTest.php deleted file mode 100644 index 4a242a44..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Tree/InTreeTest.php +++ /dev/null @@ -1,48 +0,0 @@ - root <- c2 - $graph = new Graph(); - $root = $graph->createVertex(); - - $c1 = $graph->createVertex(); - $c1->createEdgeTo($root); - - $c2 = $graph->createVertex(); - $c2->createEdgeTo($root); - - return $graph; - } - - protected function createTreeAlg(Graph $graph) - { - return new InTree($graph); - } - - protected function createGraphNonTree() - { - // v1 -> v2 <- v3 -> v4 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->createVertex('v2')); - $graph->createVertex('v3')->createEdgeTo($graph->getVertex('v2')); - $graph->getVertex('v3')->createEdgeTo($graph->createVertex('v4')); - - return $graph; - } - - protected function createGraphParallelEdge() - { - // v1 <- v2, v1 <- v2 - $graph = new Graph(); - $graph->createVertex('v2')->createEdgeTo($graph->createVertex('v1')); - $graph->getVertex('v2')->createEdgeTo($graph->getVertex('v1')); - - return $graph; - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/Tree/OutTreeTest.php b/tests/Fhaculty/Graph/Algorithm/Tree/OutTreeTest.php deleted file mode 100644 index 49283c1b..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Tree/OutTreeTest.php +++ /dev/null @@ -1,48 +0,0 @@ - c2 - $graph = new Graph(); - $root = $graph->createVertex(); - - $c1 = $graph->createVertex(); - $root->createEdgeTo($c1); - - $c2 = $graph->createVertex(); - $root->createEdgeTo($c2); - - return $graph; - } - - protected function createTreeAlg(Graph $graph) - { - return new OutTree($graph); - } - - protected function createGraphNonTree() - { - // v1 -> v3 <- v2 -> v4 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->createVertex('v3')); - $graph->createVertex('v2')->createEdgeTo($graph->getVertex('v3')); - $graph->getVertex('v2')->createEdgeTo($graph->createVertex('v4')); - - return $graph; - } - - protected function createGraphParallelEdge() - { - // v1 -> v2, v1 -> v2 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->createVertex('v2')); - $graph->getVertex('v1')->createEdgeTo($graph->getVertex('v2')); - - return $graph; - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/Tree/UndirectedTest.php b/tests/Fhaculty/Graph/Algorithm/Tree/UndirectedTest.php deleted file mode 100644 index 98c089c5..00000000 --- a/tests/Fhaculty/Graph/Algorithm/Tree/UndirectedTest.php +++ /dev/null @@ -1,114 +0,0 @@ -createTree($graph); - - $this->assertFalse($tree->isTree()); - $this->assertTrue($tree->getVerticesInternal()->isEmpty()); - $this->assertTrue($tree->getVerticesLeaf()->isEmpty()); - } - - public function testGraphTrivial() - { - $graph = new Graph(); - $graph->createVertex('v1'); - - $tree = $this->createTree($graph); - $this->assertTrue($tree->isTree()); - $this->assertSame(array(), $tree->getVerticesInternal()->getVector()); - $this->assertSame(array(), $tree->getVerticesLeaf()->getVector()); - } - - public function testGraphSimplePair() - { - // v1 -- v2 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - - $tree = $this->createTree($graph); - $this->assertTrue($tree->isTree()); - $this->assertSame(array(), $tree->getVerticesInternal()->getVector()); - $this->assertSame($graph->getVertices()->getVector(), $tree->getVerticesLeaf()->getVector()); - } - - public function testGraphSimpleLine() - { - // v1 -- v2 -- v3 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - $graph->getVertex('v2')->createEdge($graph->createVertex('v3')); - - $tree = $this->createTree($graph); - $this->assertTrue($tree->isTree()); - $this->assertSame(array($graph->getVertex('v2')), $tree->getVerticesInternal()->getVector()); - $this->assertSame(array($graph->getVertex('v1'), $graph->getVertex('v3')), $tree->getVerticesLeaf()->getVector()); - } - - public function testGraphPairParallelIsNotTree() - { - // v1 -- v2 -- v1 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - $graph->getVertex('v1')->createEdge($graph->getVertex('v2')); - - $tree = $this->createTree($graph); - $this->assertFalse($tree->isTree()); - } - - public function testGraphLoopIsNotTree() - { - // v1 -- v1 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->getVertex('v1')); - - $tree = $this->createTree($graph); - $this->assertFalse($tree->isTree()); - } - - public function testGraphCycleIsNotTree() - { - // v1 -- v2 -- v3 -- v1 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - $graph->getVertex('v2')->createEdge($graph->createVertex('v3')); - $graph->getVertex('v3')->createEdge($graph->getVertex('v1')); - - $tree = $this->createTree($graph); - $this->assertFalse($tree->isTree()); - } - - public function testGraphDirectedIsNotTree() - { - // v1 -> v2 - $graph = new Graph(); - $graph->createVertex('v1')->createEdgeTo($graph->createVertex('v2')); - - $tree = $this->createTree($graph); - $this->assertFalse($tree->isTree()); - } - - public function testGraphMixedIsNotTree() - { - // v1 -- v2 -> v3 - $graph = new Graph(); - $graph->createVertex('v1')->createEdge($graph->createVertex('v2')); - $graph->getVertex('v2')->createEdgeTo($graph->createVertex('v3')); - - $tree = $this->createTree($graph); - $this->assertFalse($tree->isTree()); - } -} diff --git a/tests/Fhaculty/Graph/Algorithm/WeightTest.php b/tests/Fhaculty/Graph/Algorithm/WeightTest.php deleted file mode 100644 index 7addf19b..00000000 --- a/tests/Fhaculty/Graph/Algorithm/WeightTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertEquals(null, $alg->getWeight()); - $this->assertEquals(0, $alg->getWeightFlow()); - $this->assertEquals(null, $alg->getWeightMin()); - $this->assertFalse($alg->isWeighted()); - - return $graph; - } - - /** - * - * @param Graph $graph - * @depends testGraphEmpty - */ - public function testGraphSimple(Graph $graph) - { - // 1 -> 2 - $graph->createVertex(1)->createEdgeTo($graph->createVertex(2))->setWeight(3)->setFlow(4); - - $alg = new AlgorithmWeight($graph); - - $this->assertEquals(3, $alg->getWeight()); - $this->assertEquals(12, $alg->getWeightFlow()); - $this->assertEquals(3, $alg->getWeightMin()); - $this->assertTrue($alg->isWeighted()); - - return $graph; - } - - /** - * - * @param Graph $graph - * @depends testGraphSimple - */ - public function testGraphWithUnweightedEdges(Graph $graph) - { - $graph->createVertex(5)->createEdgeTo($graph->createVertex(6))->setFlow(7); - - $alg = new AlgorithmWeight($graph); - - $this->assertEquals(3, $alg->getWeight()); - $this->assertEquals(12, $alg->getWeightFlow()); - $this->assertEquals(3, $alg->getWeightMin()); - $this->assertTrue($alg->isWeighted()); - } -} diff --git a/tests/Fhaculty/Graph/Loader/CompleteGraphTest.php b/tests/Fhaculty/Graph/Loader/CompleteGraphTest.php index 47649bc4..bf1ec7e6 100644 --- a/tests/Fhaculty/Graph/Loader/CompleteGraphTest.php +++ b/tests/Fhaculty/Graph/Loader/CompleteGraphTest.php @@ -1,11 +1,6 @@ assertEquals($n, count($graph->getVertices())); $this->assertEquals($n*($n-1), count($graph->getEdges())); // n*(n-1) for directed graphs - - $alg = new Directed($graph); - $this->assertTrue($alg->hasDirected()); - - $alg = new Complete($graph); - $this->assertTrue($alg->isComplete()); } } From fe8fc7c0260fbec96712fb793deb71498e632861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Thu, 26 Feb 2015 00:11:39 +0100 Subject: [PATCH 2/2] Update composer meta info to link to graphp/algorithms --- composer.json | 5 +++-- composer.lock | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 2cdcb207..b1ce0e3c 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "clue/graph", "type": "library", "description": "A mathematical graph/network library written in PHP", - "keywords": ["graph", "network", "mathematical", "vertex", "edge", "shortest path", "dijkstra", "moore-bellman-ford", "minimum spanning tree", "kruskal", "prim"], + "keywords": ["graph", "network", "mathematical", "vertex", "edge"], "homepage": "https://github.com/clue/graph", "license": "MIT", "autoload": { @@ -15,6 +15,7 @@ "phpunit/phpunit": "3.7.* | ~4.0" }, "suggest": { - "graphp/graphviz": "GraphViz graph drawing / DOT output" + "graphp/graphviz": "GraphViz graph drawing / DOT output", + "graphp/algorithms": "Common graph algorithms, such as Dijkstra and Moore-Bellman-Ford (shortest path), minimum spanning tree (MST), Kruskal, Prim and many more.." } } diff --git a/composer.lock b/composer.lock index 3bcb9deb..fc687ef1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "1acb120940c3797c111158767b8e658c", + "hash": "3374e3017236d39c8ebfa6adff220645", "packages": [], "packages-dev": [ {