/** * Traverse the graph using DFS to detect cycles * @param Graph $g * @param mixed $x0 * @return boolean Whether the graph contains cycles */ public static function containsCycle(Graph $g, $x0 = null) { //We are assuming that the graph is directed if (!$g->directed) { //If it is not, it is cyclic if it has edges return $g->size > 0; } $cyclic = false; $func = function (Vertex $v, array $discovered, array $recursionStack) use(&$cyclic) { $adjacentVertices = $v->getAdjacentVertices(); //Loop edge if (in_array($v, $adjacentVertices)) { $cyclic = true; } //This is needed for directed graphs if (array_intersect($adjacentVertices, $recursionStack) !== []) { $cyclic = true; } //Stop traversing if the graph is cyclic return !$cyclic; }; if ($x0 === null) { $vertices = $g->getSourceVertices(); if (empty($vertices)) { //If the graph has no source vertices //the graph is cyclic return true; } //The graph should be checked from every source vertex //There may be cyclic subgraphs that could be missed otherwise $visited = []; foreach ($vertices as $vertex) { $visited = array_merge($visited, static::dfs($g, $vertex->label, $func)); } //Check if there are any subgraphs without sources (not visited by above algorithm) if (count(array_unique($visited)) !== $g->order) { return true; } } else { //A specific vertex is given - check the reachable subgraph only static::dfs($g, $x0, $func); } return $cyclic; }