public function createGraph() { $this->checkBalance(); // create resulting graph with supersource and supersink $resultGraph = $this->graph->createGraphClone(); $superSource = $resultGraph->createVertex()->setLayoutAttribute('label', 's*'); $superSink = $resultGraph->createVertex()->setLayoutAttribute('label', 't*'); $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; }
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)); }