/** * @param Vertex $vertex * @return Walk */ protected function getWalkForVertex(Vertex $vertex) { if ($vertex->getId() == $this->graph->getRoot()) { return Walk::factoryFromEdges([], $vertex); } else { $shortestPath = new BreadthFirst($vertex); return $shortestPath->getWalkTo($this->vertices->getVertexLast()); } }
/** * @return string */ public function __toString() { $parts = []; /* @var Directed $edge */ foreach ($this->walk->getEdges() as $edge) { $from = $this->getEntityClassName($edge->getVertexStart()->getId()); $to = $this->getEntityClassName($edge->getVertexEnd()->getId()); $parts[] = sprintf(' %s %s %s', str_pad($from, 20, ' ', STR_PAD_LEFT), str_pad($edge->getLayoutAttribute('label'), 15, '-', STR_PAD_BOTH) . '>', $to); } return implode("\n", $parts); }
/** * 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); }
/** * 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 (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 testFactoryFromVertices() { // 1 -- 2 // \----/ $graph = new Graph(); $v1 = $graph->createVertex(1); $v2 = $graph->createVertex(2); $e1 = $v1->createEdge($v2)->setWeight(10); $e2 = $v1->createEdge($v2)->setWeight(20); // any edge in walk $walk = Walk::factoryFromVertices(array($v1, $v2)); // edge with weight 10 $walk = Walk::factoryFromVertices(array($v1, $v2), Edges::ORDER_WEIGHT); $this->assertSame($e1, $walk->getEdges()->getEdgeFirst()); // edge with weight 20 $walk = Walk::factoryFromVertices(array($v1, $v2), Edges::ORDER_WEIGHT, true); $this->assertSame($e2, $walk->getEdges()->getEdgeFirst()); }
/** * 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()); }
/** * Store properties to be cleared * * Because we're only exporting data, we don't need to actually update * the data at all for the clear to be respected. We just need to modify * what we write to the exported files. * * @param \Fhaculty\Graph\Walk $walk * @param array $fields * @param array $joinFields * @return mixed */ public function processClear(Walk $walk, array $fields, array $joinFields) { $this->clear[$walk->getVertexSource()->getId()] = ['fields' => $fields, 'joinFields' => $joinFields]; }
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()); }
/** * @expectedException InvalidArgumentException */ public function testInvalidPredecessors() { $graph = new Graph(); $v1 = $graph->createVertex(1); Walk::factoryCycleFromPredecessorMap(array(), $v1); }