コード例 #1
0
ファイル: CycleCanceling.php プロジェクト: feffi/graph
 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;
 }
コード例 #2
0
ファイル: EdmondsKarp.php プロジェクト: feffi/graph
 /**
  * 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;
 }
コード例 #3
0
ファイル: ResidualGraphTest.php プロジェクト: feffi/graph
 /**
  * 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();
 }
コード例 #4
0
 /**
  * @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;
 }