public function getEdges() { $alg = new Directed($this->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); }
public function createGraph() { $this->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; }
/** * @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()); }