/** * check whether this graph has an eulerian cycle * * @return boolean * @uses ConnectedComponents::isSingle() * @uses Degree::getDegreeVertex() * @todo isolated vertices should be ignored * @todo definition is only valid for undirected graphs */ public function hasCycle() { $components = new ConnectedComponents($this->graph); if ($components->isSingle()) { $alg = new Degree($this->graph); foreach ($this->graph->getVertices() as $vertex) { // uneven degree => fail if ($alg->getDegreeVertex($vertex) & 1) { return false; } } return true; } return false; }
public function testGraphIrregular() { // 1 -> 2 -> 3 $graph = new Graph(); $v1 = $graph->createVertex(1); $v2 = $graph->createVertex(2); $v3 = $graph->createVertex(3); $v1->createEdgeTo($v2); $v2->createEdgeTo($v3); $alg = new AlgorithmDegree($graph); try { $this->assertEquals(0, $alg->getDegree()); $this->fail(); } catch (UnexpectedValueException $e) { } $this->assertEquals(1, $alg->getDegreeMin()); $this->assertEquals(2, $alg->getDegreeMax()); $this->assertFalse($alg->isRegular()); $this->assertFalse($alg->isBalanced()); $this->assertEquals(0, $alg->getDegreeInVertex($v1)); $this->assertEquals(1, $alg->getDegreeOutVertex($v1)); $this->assertEquals(1, $alg->getDegreeVertex($v1)); $this->assertFalse($alg->isVertexIsolated($v1)); $this->assertFalse($alg->isVertexSink($v1)); $this->assertTrue($alg->isVertexSource($v1)); $this->assertEquals(1, $alg->getDegreeInVertex($v2)); $this->assertEquals(1, $alg->getDegreeOutVertex($v2)); $this->assertEquals(2, $alg->getDegreeVertex($v2)); $this->assertFalse($alg->isVertexIsolated($v2)); $this->assertFalse($alg->isVertexSink($v2)); $this->assertFalse($alg->isVertexSource($v2)); $this->assertEquals(1, $alg->getDegreeInVertex($v3)); $this->assertEquals(0, $alg->getDegreeOutVertex($v3)); $this->assertEquals(1, $alg->getDegreeVertex($v3)); $this->assertFalse($alg->isVertexIsolated($v3)); $this->assertTrue($alg->isVertexSink($v3)); $this->assertFalse($alg->isVertexSource($v3)); }
/** * create graphviz script representing this graph * * @param Graph $graph graph to display * @return string * @uses Directed::hasDirected() * @uses Graph::getVertices() * @uses Graph::getEdges() */ public function createScript(Graph $graph) { $alg = new Directed($graph); $directed = $alg->hasDirected(); $script = ($directed ? 'di' : '') . 'graph G {' . self::EOL; // add global attributes $globals = array('graph' => 'graphviz.graph.', 'node' => 'graphviz.node.', 'edge' => 'graphviz.edge.'); foreach ($globals as $key => $prefix) { $bag = new AttributeBagNamespaced($graph, $prefix); if ($layout = $bag->getAttributes()) { $script .= $this->formatIndent . $key . ' ' . $this->escapeAttributes($layout) . self::EOL; } } $alg = new Groups($graph); // only append group number to vertex label if there are at least 2 different groups $showGroups = $alg->getNumberOfGroups() > 1; if ($showGroups) { $gid = 0; $indent = str_repeat($this->formatIndent, 2); // put each group of vertices in a separate subgraph cluster foreach ($alg->getGroups() as $group) { $script .= $this->formatIndent . 'subgraph cluster_' . $gid++ . ' {' . self::EOL . $indent . 'label = ' . $this->escape($group) . self::EOL; foreach ($alg->getVerticesGroup($group)->getMap() as $vid => $vertex) { $layout = $this->getLayoutVertex($vertex); $script .= $indent . $this->escapeId($vid); if ($layout) { $script .= ' ' . $this->escapeAttributes($layout); } $script .= self::EOL; } $script .= ' }' . self::EOL; } } else { $alg = new Degree($graph); // explicitly add all isolated vertices (vertices with no edges) and vertices with special layout set // other vertices wil be added automatically due to below edge definitions foreach ($graph->getVertices()->getMap() as $vid => $vertex) { $layout = $this->getLayoutVertex($vertex); if ($layout || $alg->isVertexIsolated($vertex)) { $script .= $this->formatIndent . $this->escapeId($vid); if ($layout) { $script .= ' ' . $this->escapeAttributes($layout); } $script .= self::EOL; } } } $edgeop = $directed ? ' -> ' : ' -- '; // add all edges as directed edges foreach ($graph->getEdges() as $currentEdge) { $both = $currentEdge->getVertices()->getVector(); $currentStartVertex = $both[0]; $currentTargetVertex = $both[1]; $script .= $this->formatIndent . $this->escapeId($currentStartVertex->getId()) . $edgeop . $this->escapeId($currentTargetVertex->getId()); $layout = $this->getLayoutEdge($currentEdge); // this edge also points to the opposite direction => this is actually an undirected edge if ($directed && $currentEdge->isConnection($currentTargetVertex, $currentStartVertex)) { $layout['dir'] = 'none'; } if ($layout) { $script .= ' ' . $this->escapeAttributes($layout); } $script .= self::EOL; } $script .= '}' . self::EOL; return $script; }