/** * 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'); }
/** * @uses Graph::createGraphClone() * @uses ResidualGraph::createGraph() * @uses SpMooreBellmanFord::getEdgesTo(Vertex $targetVertex) * @see Base::createGraph() */ public function createGraph() { $this->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 MooreBellmanFord $alg * @depends testGraphParallelNegative * @expectedException UnderflowException */ public function testNoNegativeCycle(MooreBellmanFord $alg) { $alg->getCycleNegative(); }