/** * create subgraph with all vertices connected to given vertex (i.e. the connected component of ths given vertex) * * @param Vertex $vertex * @return Graph * @throws InvalidArgumentException if given vertex is not from same graph * @uses AlgorithmSearchBreadthFirst::getVertices() * @uses Graph::createGraphCloneVertices() */ public function createGraphComponentVertex(Vertex $vertex) { if ($vertex->getGraph() !== $this->graph) { throw new InvalidArgumentException('This graph does not contain the given vertex'); } return $this->graph->createGraphCloneVertices($this->createSearch($vertex)->getVertices()); }
protected function getVerticesParent(Vertex $vertex) { $vertices = $vertex->getVerticesEdgeFrom(); if ($vertices->hasDuplicates()) { throw new UnexpectedValueException(); } return $vertices; }
public function getIndexVertex(Vertex $vertex) { $id = $vertex->getId(); if (!isset($this->vertices[$id]) || $this->vertices[$id] !== $vertex) { throw new OutOfBoundsException(); } return $id; }
/** * checks whether this vertex has a loop (edge to itself) * * @return boolean * @uses Edge::isLoop() */ public function hasLoopVertex(Vertex $vertex) { foreach ($vertex->getEdges() as $edge) { if ($edge->isLoop()) { return true; } } return false; }
/** * @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()); } }
public function getEdgesTo(Vertex $endVertex) { if ($endVertex->getGraph() === $this->vertex->getGraph()) { $map = $this->getEdgesMap(); if (isset($map[$endVertex->getId()])) { return new Edges($map[$endVertex->getId()]); } } throw new OutOfBoundsException('Given target vertex can not be reached from start vertex'); }
/** * create a new undirected edge between given vertices * * @param Vertex $a * @param Vertex $b * @see Vertex::createEdge() instead */ public function __construct(Vertex $a, Vertex $b) { if ($a->getGraph() !== $b->getGraph()) { throw new InvalidArgumentException('Vertices have to be within the same graph'); } $this->a = $a; $this->b = $b; $a->getGraph()->addEdge($this); $a->addEdge($this); $b->addEdge($this); }
/** * create a new directed Edge from Vertex $from to Vertex $to * * @param Vertex $from start/source Vertex * @param Vertex $to end/target Vertex * @see Vertex::createEdgeTo() to create directed edges * @see Vertex::createEdge() to create undirected edges */ public function __construct(Vertex $from, Vertex $to) { if ($from->getGraph() !== $to->getGraph()) { throw new InvalidArgumentException('Vertices have to be within the same graph'); } $this->from = $from; $this->to = $to; $from->getGraph()->addEdge($this); $from->addEdge($this); $to->addEdge($this); }
protected function getVerticesAdjacent(Vertex $vertex) { if ($this->direction === self::DIRECTION_FORWARD) { return $vertex->getVerticesEdgeTo(); } elseif ($this->direction === self::DIRECTION_REVERSE) { return $vertex->getVerticesEdgeFrom(); } elseif ($this->direction === self::DIRECTION_BOTH) { return $vertex->getVerticesEdge(); } else { throw new DomainException('Should not happen. Invalid direction setting'); } }
/** * * calculates the recursive algorithm * * fills $this->visitedVertices * * @param Vertex $vertex */ private function recursiveDepthFirstSearch(Vertex $vertex, array &$visitedVertices) { // If I didn't visited this vertex before if (!isset($visitedVertices[$vertex->getId()])) { // Add Vertex to already visited vertices $visitedVertices[$vertex->getId()] = $vertex; // Get next vertices $nextVertices = $vertex->getVerticesEdgeTo(); foreach ($nextVertices as $nextVertix) { // recursive call for next vertices $this->recursiveDepthFirstSearch($nextVertix, $visitedVertices); } } }
/** * create new cycle instance from given predecessor map * * @param Vertex[] $predecessors map of vid => predecessor vertex instance * @param Vertex $vertex start vertex to search predecessors from * @param int|null $by * @param boolean $desc * @return Walk * @throws UnderflowException * @see Edges::getEdgeOrder() for parameters $by and $desc * @uses self::factoryFromVertices() */ public static function factoryCycleFromPredecessorMap(array $predecessors, Vertex $vertex, $by = null, $desc = false) { // find a vertex in the cycle $vid = $vertex->getId(); $startVertices = array(); do { if (!isset($predecessors[$vid])) { throw new InvalidArgumentException('Predecessor map is incomplete and does not form a cycle'); } $startVertices[$vid] = $vertex; $vertex = $predecessors[$vid]; $vid = $vertex->getId(); } while (!isset($startVertices[$vid])); // find negative cycle $vid = $vertex->getId(); // build array of vertices in cycle $vertices = array(); do { // add new vertex to cycle $vertices[$vid] = $vertex; // get predecessor of vertex $vertex = $predecessors[$vid]; $vid = $vertex->getId(); // continue until we find a vertex that's already in the circle (i.e. circle is closed) } while (!isset($vertices[$vid])); // reverse cycle, because cycle is actually built in opposite direction due to checking predecessors $vertices = array_reverse($vertices, true); // additional edge from last vertex to first vertex $vertices[] = reset($vertices); return self::factoryCycleFromVertices($vertices, $by, $desc); }
/** * Calculates the flow for this Vertex: sum(outflow) - sum(inflow) * * Usually, vertices should have a resulting flow of 0: The sum of flows * entering a vertex must equal the sum of flows leaving a vertex. If the * resulting flow is < 0, this vertex is considered a sink (i.e. there's * more flow into this vertex). If the resulting flow is > 0, this vertex * is considered a "source" (i.e. there's more flow leaving this vertex). * * @param Vertex $vertex * @return float * @throws UnexpectedValueException if they are undirected edges * @see Vertex::getBalance() * @uses Vertex::getEdges() * @uses Edge::getFlow() */ public function getFlowVertex(Vertex $vertex) { $sumOfFlow = 0; foreach ($vertex->getEdges() as $edge) { if (!$edge instanceof EdgeDirected) { throw new UnexpectedValueException("TODO: undirected edges not suported yet"); } // edge is an outgoing edge of this vertex if ($edge->hasVertexStart($vertex)) { // flowing out (flow is "pointing away") $sumOfFlow += $edge->getFlow(); // this is an ingoing edge } else { // flowing in $sumOfFlow -= $edge->getFlow(); } } return $sumOfFlow; }
/** * Returns max flow value * * @return double */ public function getFlowMax() { $resultGraph = $this->createGraph(); $start = $resultGraph->getVertex($this->startVertex->getId()); $maxFlow = 0; foreach ($start->getEdgesOut() as $edge) { $maxFlow = $maxFlow + $edge->getFlow(); } return $maxFlow; }
/** * @param Vertex $vertex * @return \Fhaculty\Graph\Edge\Directed */ private function createEdgeToAdtRootVertexBy(Vertex $vertex) { foreach ($this->adtRootVertex->getEdges() as $edge) { /** @var \Fhaculty\Graph\Edge\Directed $edge */ if ($edge->isConnection($this->adtRootVertex, $vertex)) { return $edge; } } return $this->adtRootVertex->createEdgeTo($vertex); }
/** * get label for given $vertex * * @param Vertex $vertex * @return string */ protected function getVertexLabel(Vertex $vertex) { // label defaults to the vertex ID $label = $vertex->getId(); // add balance to label if set $balance = $vertex->getBalance(); if ($balance !== NULL) { if ($balance > 0) { $balance = '+' . $balance; } $label .= ' (' . $balance . ')'; } // add group to label if set // TODO: what does 'if set' mean? groups should not be shown when vertex never had any group assigned (but it defaults to 0) // $group = $vertex->getGroup(); // if ($group !== 0) { // $label .= ' [' . $group .']'; // } return $label; }
protected function visit(Vertex $vertex, array &$visited, array &$tsl) { $vid = $vertex->getId(); if (isset($visited[$vid])) { if ($visited[$vid] === false) { // temporary mark => not a DAG throw new UnexpectedValueException('Not a DAG'); } // otherwise already marked/visisted => no need to check again } else { // temporary mark $visited[$vid] = false; foreach (array_reverse($vertex->getVerticesEdgeTo()->getVector()) as $v) { $this->visit($v, $visited, $tsl); } // mark as visited and include in result $visited[$vid] = true; $tsl[$vid] = $vertex; } }
/** * * @return Edges */ public function getEdges() { $returnEdges = array(); $n = count($this->vertex->getGraph()->getVertices()); $vertex = $this->vertex; $visitedVertices = array($vertex->getId() => true); for ($i = 0; $i < $n - 1; ++$i, $vertex = $nextVertex) { // get all edges from the aktuel vertex $edges = $vertex->getEdgesOut(); $sortedEdges = new SplPriorityQueue(); // sort the edges foreach ($edges as $edge) { $sortedEdges->insert($edge, -$edge->getWeight()); } // Untill first is found: get cheepest edge foreach ($sortedEdges as $edge) { // Get EndVertex of this edge $nextVertex = $edge->getVertexToFrom($vertex); // is unvisited if (!isset($visitedVertices[$nextVertex->getId()])) { break; } } // check if there is a way i can use if (isset($visitedVertices[$nextVertex->getId()])) { throw new UnexpectedValueException('Graph is not complete - can\'t find an edge to unconnected vertex'); } $visitedVertices[$nextVertex->getId()] = TRUE; // clone edge in new Graph $returnEdges[] = $edge; } // check if there is a way from end edge to start edge // get first connecting edge // connect the last vertex with the start vertex $returnEdges[] = $vertex->getEdgesTo($this->vertex)->getEdgeFirst(); return new Edges($returnEdges); }
/** * get neighbor vertices for given start vertex * * @param Vertex $vertex * @throws UnexpectedValueException for directed edges * @return Vertices (might include possible duplicates) * @uses Vertex::getEdges() * @uses Edge::getVertexToFrom() * @see Vertex::getVerticesEdge() */ private function getVerticesNeighbor(Vertex $vertex) { $vertices = array(); foreach ($vertex->getEdges() as $edge) { /* @var Edge $edge */ if (!$edge instanceof UndirectedEdge) { throw new UnexpectedValueException('Directed edge encountered'); } $vertices[] = $edge->getVertexToFrom($vertex); } return new Vertices($vertices); }
/** * get outdegree of this vertex (number of edges FROM this vertex TO other vertices) * * @param Vertex $vertex * @return int * @uses Edge::hasVertexStart() * @see self::getDegreeVertex() */ public function getDegreeOutVertex(Vertex $vertex) { $n = 0; foreach ($vertex->getEdges() as $edge) { if ($edge->hasVertexStart($vertex)) { ++$n; } } return $n; }
/** * @param Vertex $vertex * @return array */ private function extractVertex(Vertex $vertex) { $locations = $this->extractEntities($vertex->getAttribute('locations', array())); return array('name' => $vertex->getId(), 'usedByCount' => $vertex->getEdgesIn()->count(), 'adt' => $vertex->getAttribute('adt', array()), 'location' => array_shift($locations), 'group' => $vertex->getGroup()); }
protected function createAlg(Vertex $vertex) { return new Kruskal($vertex->getGraph()); }
/** * helper method to get recursively get subtree for given $vertex * * @param Vertex $vertex * @param Vertex[] $vertices * @throws UnexpectedValueException if multiple links were found to the given edge (check isTree()!) * @uses self::getVerticesChildren() * @uses self::getVerticesSubtreeRecursive() to recurse into subtrees */ private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices) { $vid = $vertex->getId(); if (isset($vertices[$vid])) { throw new UnexpectedValueException('Multiple links found'); } $vertices[$vid] = $vertex; foreach ($this->getVerticesChildren($vertex) as $vertexChild) { $this->getVerticesSubtreeRecursive($vertexChild, $vertices); } }
private function addBalance(Vertex $vertex, $balance) { $vertex->setBalance($vertex->getBalance() + $balance); }
private function getEdgeCloneInternal(Edge $edge, Vertex $startVertex, Vertex $targetVertex) { // Get original vertices from resultgraph $residualGraphEdgeStartVertex = $this->getVertex($startVertex->getId()); $residualGraphEdgeTargetVertex = $this->getVertex($targetVertex->getId()); // Now get the edge $residualEdgeArray = $residualGraphEdgeStartVertex->getEdgesTo($residualGraphEdgeTargetVertex); $residualEdgeArray = Edges::factory($residualEdgeArray)->getVector(); // Check for parallel edges if (!$residualEdgeArray) { throw new UnderflowException('No original edges for given cloned edge found'); } elseif (count($residualEdgeArray) !== 1) { throw new OverflowException('More than one cloned edge? Parallel edges (multigraph) not supported'); } return $residualEdgeArray[0]; }
/** * get set of Edges FROM the given vertex TO this vertex * * @param Vertex $vertex * @return Edges * @uses Vertex::getEdgesTo() */ public function getEdgesFrom(Vertex $vertex) { return $vertex->getEdgesTo($this); }
protected function getLayoutVertex(Vertex $vertex) { $layout = $vertex->getLayout(); $balance = $vertex->getBalance(); if ($balance !== NULL) { if ($balance > 0) { $balance = '+' . $balance; } if (!isset($layout['label'])) { $layout['label'] = $vertex->getId(); } $layout['label'] .= ' (' . $balance . ')'; } return $layout; }
protected function getGraph() { return $this->startVertex->getGraph(); }
/** * adds a new Vertex to the Graph (MUST NOT be called manually!) * * @param Vertex $vertex instance of the new Vertex * @return void * @private * @see self::createVertex() instead! */ public function addVertex(Vertex $vertex) { assert($vertex instanceof Vertex); if (isset($this->verticesStorage[$vertex->getId()])) { throw new OverflowException('ID must be unique'); } $this->verticesStorage[$vertex->getId()] = $vertex; }
/** * * @param Vertex $vertex current point-of-view * @param number $totalWeight total weight (so far) * @param boolean[] $visitedVertices * @param Edge[] $visitedEdges * @return Edge[] */ private function step(Vertex $vertex, $totalWeight, array $visitedVertices, array $visitedEdges) { // stop recursion if best result is exceeded (branch and bound) if ($this->branchAndBound && $this->bestWeight !== NULL && $totalWeight >= $this->bestWeight) { return NULL; } // kreis geschlossen am Ende if ($vertex === $this->startVertex && count($visitedEdges) === $this->numEdges) { // new best result $this->bestWeight = $totalWeight; return $visitedEdges; } // only visit each vertex once if (isset($visitedVertices[$vertex->getId()])) { return NULL; } $visitedVertices[$vertex->getId()] = true; $bestResult = NULL; // weiter verzweigen in alle vertices foreach ($vertex->getEdgesOut() as $edge) { // get target vertex of this edge $target = $edge->getVertexToFrom($vertex); $weight = $edge->getWeight(); if ($weight < 0) { throw new UnexpectedValueException('Edge with negative weight "' . $weight . '" not supported'); } $result = $this->step($target, $totalWeight + $weight, $visitedVertices, array_merge($visitedEdges, array($edge))); // new result found if ($result !== NULL) { // branch and bound enabled (default): returned result MUST be the new best result if ($this->branchAndBound || $bestResult === NULL || $this->sumEdges($result) < $this->sumEdges($bestResult)) { $bestResult = $result; } } } return $bestResult; }
protected function getLayoutVertex(Vertex $vertex) { $bag = new AttributeBagNamespaced($vertex, 'graphviz.'); $layout = $bag->getAttributes(); $balance = $vertex->getBalance(); if ($balance !== NULL) { if ($balance > 0) { $balance = '+' . $balance; } if (!isset($layout['label'])) { $layout['label'] = $vertex->getId(); } $layout['label'] .= ' (' . $balance . ')'; } return $layout; }