Commit e64a4c79 authored by webchick's avatar webchick

Issue #2498599 by Cottser, naveenvalecha, webchick, karolus, cilefen: Remove...

Issue #2498599 by Cottser, naveenvalecha, webchick, karolus, cilefen: Remove sdboyer/gliph since it is unused by core
parent a8f8b33e
......@@ -5,7 +5,6 @@
"license": "GPL-2.0+",
"require": {
"php": ">=5.5.9",
"sdboyer/gliph": "0.1.*",
"symfony/class-loader": "2.7.*",
"symfony/console": "2.7.*",
"symfony/css-selector": "2.7.*",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "34a206d03b9060c6f1b2895c0517792c",
"hash": "3708d8fdb54957e5ce661cda1df88353",
"packages": [
{
"name": "behat/mink",
......@@ -1637,54 +1637,6 @@
],
"time": "2012-12-21 11:40:51"
},
{
"name": "sdboyer/gliph",
"version": "0.1.8",
"source": {
"type": "git",
"url": "https://github.com/sdboyer/gliph.git",
"reference": "db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sdboyer/gliph/zipball/db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e",
"reference": "db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"satooshi/php-coveralls": "0.6.*"
},
"type": "library",
"autoload": {
"psr-0": {
"Gliph": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sam Boyer",
"email": "tech@samboyer.org"
}
],
"description": "A graph library for PHP.",
"homepage": "http://github.com/sdboyer/gliph",
"keywords": [
"gliph",
"graph",
"library",
"php",
"spl"
],
"time": "2014-08-03 14:34:47"
},
{
"name": "sebastian/comparator",
"version": "1.1.1",
......
......@@ -12,7 +12,6 @@
'Stack' => array($vendorDir . '/stack/builder/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'),
'Gliph' => array($vendorDir . '/sdboyer/gliph/src'),
'Egulias\\' => array($vendorDir . '/egulias/email-validator/src'),
'EasyRdf_' => array($vendorDir . '/easyrdf/easyrdf/lib'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src'),
......
......@@ -423,56 +423,6 @@
"timer"
]
},
{
"name": "sdboyer/gliph",
"version": "0.1.8",
"version_normalized": "0.1.8.0",
"source": {
"type": "git",
"url": "https://github.com/sdboyer/gliph.git",
"reference": "db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sdboyer/gliph/zipball/db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e",
"reference": "db9e4b77622f91e2d338cc45f83c2cd0e3cf0e1e",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"satooshi/php-coveralls": "0.6.*"
},
"time": "2014-08-03 14:34:47",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Gliph": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sam Boyer",
"email": "tech@samboyer.org"
}
],
"description": "A graph library for PHP.",
"homepage": "http://github.com/sdboyer/gliph",
"keywords": [
"gliph",
"graph",
"library",
"php",
"spl"
]
},
{
"name": "psr/log",
"version": "1.0.0",
......
Copyright (c) Sam Boyer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Gliph
[![Build Status](https://travis-ci.org/sdboyer/gliph.png?branch=php53)](https://travis-ci.org/sdboyer/gliph)
[![Latest Stable Version](https://poser.pugx.org/sdboyer/gliph/v/stable.png)](https://packagist.org/packages/sdboyer/gliph)
[![Coverage Status](https://coveralls.io/repos/sdboyer/gliph/badge.png?branch=php53)](https://coveralls.io/r/sdboyer/gliph?branch=php53)
Gliph is a **g**raph **li**brary for **PH**P. It provides graph building blocks and datastructures for use by other PHP applications. It is (currently) designed for use with in-memory graphs, not for interaction with a graph database like [Neo4J](http://neo4j.org/).
Gliph aims for both sane interfaces and performant implementation - at least, as performant as can be hoped for a PHP graph library. This does require knowing enough about graphs to know what type is appropriate for your use case, but we are aiming to provide helpers that simplify those choices.
## Core Concepts
Gliph has several components that work together: graph classes, algorithms, and visitors. Generally speaking, Gliph is patterned after the [C++ Boost Graph Library](http://www.boost.org/libs/graph/doc); reading their documentation can yield a lot of insight into how Gliph is intended to work.
Note that Gliph is currently written for compatibility with PHP 5.3, but it is intended to port the library to PHP 5.5. The availability of traits, non-scalar keys in iterators, and generators will considerably change and improve both the internal and public-facing implementations.
### Graphs
There are a number of different strategies for representing graphs; these strategies are more or less efficient depending on certain properties the graph, and what needs to be done to the graph. The approach taken in Gliph is to offer a roughly consistent 'Graph' interface that is common to all these different strategies. The strategies will have varying levels of efficiency at meeting this common interface, so it is the responsibility of the user to select a graph implementation that is appropriate for their use case. This approach draws heavily from the [taxonomy of graphs](http://www.boost.org/doc/libs/1_54_0/libs/graph/doc/graph_concepts.html) established by the BGL.
Gliph currently implements only an adjacency list graph strategy, in both directed and undirected flavors. Adjacency lists offer efficient access to out-edges, but inefficient access to in-edges (in a directed graph - in an undirected graph, in-edges and out-edges are the same). Adjacency lists and are generally more space-efficient for sparse graphs.
### Algorithms
Gliph provides various algorithms that can be run on graph objects. These algorithms interact with the graph by making calls to methods defined in the assorted Graph interfaces. If a graph implements the interface type-hinted by a particular algorithm, then the algorithm can run on that graph. But the efficiency of the algorithm will be largely determined by how efficiently that graph implementation can meet the requirements of the interface. Adjacency lists, for example, are not terribly efficient at providing a list of all edges in a graph, but are very good at single-vertex-centric operations.
Gliph's algorithms are typically implemented quite sparsely (especially traversers) - they seek to implement the simplest, most generic version of an algorithm. They also may not return any output, as that work is left to Visitors.
### Visitors
Most algorithms require a visitor object to be provided. The visitor conforms to an interface specified by the algorithm, and the algorithm will call the visitor at certain choice points during its execution. This allows the algorithms to stay highly generic, while visitors can be tailored to a more specific purpose.
For example, a ```DepthFirst``` visitor might be used to calculate vertex reach, or generate a topologically sorted list. Each of these are things that a depth-first graph traversal can do. But the work of doing so is left to the visitor so that only one traversal algorithm is needed, and that algorithm is as cheap (memory and cycles) as possible.
## TODOs
Lots. But, to start with:
- Port to, or provide a parallel implementation in, PHP 5.5. Generators and non-scalar keys from iterators make this all SO much better. In doing that, also shift as much over to traits as possible.
- Implement a generic breadth-first algorithm and its corresponding visitors.
- Implement a generic iterative deepening depth-first algorithm, and its corresponding visitors.
- Implement other popular connected components algorithms, as well as some shortest path algorithms (starting with Dijkstra)
- Write up some examples showing how to actually use the library.
- Extend the ```DepthFirst::traverse()``` algorithm and ```DepthFirstVisitorInterface``` to allow the visitor to stop the traversal. useful if, e.g., a search is being performed and the desired vertex has been found.
## Acknowledgements
This library draws heavy inspiration from the [C++ Boost Graph Library](http://www.boost.org/libs/graph/doc).
## License
MIT
{
"name": "sdboyer/gliph",
"description": "A graph library for PHP.",
"license": "MIT",
"keywords": ["gliph", "library", "php", "spl", "graph"],
"homepage": "http://github.com/sdboyer/gliph",
"type": "library",
"authors": [
{
"name": "Sam Boyer",
"email": "tech@samboyer.org"
}
],
"require": {
"php": ">=5.3"
},
"require-dev": {
"satooshi/php-coveralls": "0.6.*",
"phpunit/phpunit": "3.7.*"
},
"autoload": {
"psr-0": { "Gliph": "src/" }
}
}
This diff is collapsed.
<?php
namespace Gliph\Algorithm;
use Gliph\Graph\DirectedGraph;
use Gliph\Visitor\TarjanSCCVisitor;
/**
* Contains algorithms for discovering connected components.
*/
class ConnectedComponent {
/**
* Finds connected components in the provided directed graph.
*
* @param DirectedGraph $graph
* The DirectedGraph to search for connected components.
* @param TarjanSCCVisitor $visitor
* The visitor that will collect and store the connected components. One
* will be created if not provided.
*
* @return TarjanSCCVisitor
* The finalized visitor.
*/
public static function tarjan_scc(DirectedGraph $graph, TarjanSCCVisitor $visitor = NULL) {
$visitor = $visitor ?: new TarjanSCCVisitor();
$counter = 0;
$stack = array();
$indices = new \SplObjectStorage();
$lowlimits = new \SplObjectStorage();
$visit = function($vertex) use (&$visit, &$counter, $graph, &$stack, $indices, $lowlimits, $visitor) {
$indices->attach($vertex, $counter);
$lowlimits->attach($vertex, $counter);
$stack[] = $vertex;
$counter++;
$graph->eachAdjacent($vertex, function ($to) use (&$visit, $vertex, $indices, $lowlimits, &$stack) {
if (!$indices->contains($to)) {
$visit($to);
$lowlimits[$vertex] = min($lowlimits[$vertex], $lowlimits[$to]);
}
else if (in_array($to, $stack, TRUE)) {
$lowlimits[$vertex] = min($lowlimits[$vertex], $indices[$to]);
}
});
if ($lowlimits[$vertex] === $indices[$vertex]) {
$visitor->newComponent();
do {
$other = array_pop($stack);
$visitor->addToCurrentComponent($other);
} while ($other != $vertex);
}
};
$graph->eachVertex(function($vertex) use (&$visit, $indices) {
if (!$indices->contains($vertex)) {
$visit($vertex);
}
});
return $visitor;
}
}
\ No newline at end of file
<?php
/**
* @file
* Contains \Gliph\Exception\InvalidVertexTypeException.
*/
namespace Gliph\Exception;
/**
* Error thrown when attempting to add a vertex of an invalid type.
*/
class InvalidVertexTypeException extends \Exception {}
\ No newline at end of file
<?php
/**
* @file
* Contains \Gliph\Exception\NonexistentVertexException.
*/
namespace Gliph\Exception;
/**
* Exception thrown when a vertex not present in a Graph is provided as a
* parameter to a method that requires the vertex to be present (e.g., removing
* the vertex, checking the edges of that vertex).
*/
class NonexistentVertexException extends \OutOfBoundsException {}
\ No newline at end of file
<?php
namespace Gliph\Exception;
/**
* OutOfRangeException for Gliph.
*/
class OutOfRangeException extends \OutOfRangeException {}
\ No newline at end of file
<?php
namespace Gliph\Exception;
/**
* RuntimeException for Gliph.
*/
class RuntimeException extends \RuntimeException {}
<?php
namespace Gliph\Exception;
/**
* An exception thrown when a method is called on a visitor that it does not
* expect in its current state.
*
* For example, this exception should be thrown by a visitor if it has a method
* that returns data produced by a full traversal algorithm, but the algorithm
* has not yet informed the visitor that it is done running.
*/
class WrongVisitorStateException extends \LogicException {}
\ No newline at end of file
<?php
namespace Gliph\Graph;
use Gliph\Exception\InvalidVertexTypeException;
use Gliph\Exception\NonexistentVertexException;
/**
* A graph, represented as an adjacency list.
*
* Adjacency lists store vertices directly, and edges relative to the vertices
* they connect. That means there is no overall list of edges in the graph; only
* a list of the graph's vertices. In this implementation, that list is keyed by
* vertex, with the value being a list of all the vertices to which that vertex
* is adjacent - hence, "adjacency list."
*
* Consequently, this structure offers highly efficient access to vertices, but
* less efficient access to edges.
*
* In an undirected graph, the edges are stored in both vertices' adjacency
* lists. In a directed graph, only the out-edges are stored in each vertex's
* adjacency list. This makes accessing in-edge information in a directed graph
* highly inefficient.
*/
abstract class AdjacencyList implements MutableGraph {
/**
* Contains the adjacency list of vertices.
*
* @var \SplObjectStorage
*/
protected $vertices;
/**
* Bookkeeper for nested iteration.
*
* @var \SplObjectStorage
*/
protected $walking;
/**
* Count of the number of edges in the graph.
*
* We keep track because calculating it on demand is expensive.
*
* @var int
*/
protected $size = 0;
public function __construct() {
$this->vertices = new \SplObjectStorage();
$this->walking = new \SplObjectStorage();
}
/**
* {@inheritdoc}
*/
public function addVertex($vertex) {
if (!is_object($vertex)) {
throw new InvalidVertexTypeException('Vertices must be objects; non-object provided.');
}
if (!$this->hasVertex($vertex)) {
$this->vertices[$vertex] = new \SplObjectStorage();
}
return $this;
}
/**
* {@inheritdoc}
*/
public function eachAdjacent($vertex, $callback) {
if (!$this->hasVertex($vertex)) {
throw new NonexistentVertexException('Vertex is not in graph; cannot iterate over its adjacent vertices.');
}
$set = $this->_getTraversableSplos($this->vertices[$vertex]);
foreach ($set as $adjacent_vertex) {
call_user_func($callback, $adjacent_vertex);
}
$this->walking->detach($set);
return $this;
}
/**
* {@inheritdoc}
*/
public function eachVertex($callback) {
$this->fev(function ($v, $adjacent) use ($callback) {
call_user_func($callback, $v, $adjacent);
});
return $this;
}
/**
* {@inheritdoc}
*/
public function hasVertex($vertex) {
return $this->vertices->contains($vertex);
}
/**
* {@inheritdoc}
*/
public function order() {
return $this->vertices->count();
}
/**
* {@inheritdoc}
*/
public function size() {
return $this->size;
}
protected function fev($callback) {
$set = $this->_getTraversableSplos($this->vertices);
foreach ($set as $vertex) {
$outgoing = $set->getInfo();
$callback($vertex, $outgoing);
}
$this->walking->detach($set);
return $this;
}
/**
* Helper function to ensure SPLOS traversal pointer is not overridden.
*
* This would otherwise occur if nested calls are made that traverse the
* same SPLOS. This keeps track of which SPLOSes are currently being
* traversed, and if it's in use, it returns a clone.
*
* It is incumbent on the calling code to release the semaphore directly
* by calling $this->_cleanupSplosTraversal() when the traversal in
* question is complete. (This is very important!)
*
* Only public because it needs to be called from within closures.
*
* @param \SplObjectStorage $splos
* The SPLOS to traverse.
*
* @return \SplObjectStorage
* A SPLOS that is safe for traversal; may or may not be a clone of the
* original.
*/
public function _getTraversableSplos(\SplObjectStorage $splos) {
if ($this->walking->contains($splos)) {
return clone $splos;
}
else {
$this->walking->attach($splos);
return $splos;
}
}
/**
* Helper function to clean up SPLOSes after finishing traversal.
*
* @param \SplObjectStorage $splos
* The SPLOS to mark as safe for traversal again.
*/
public function _cleanupSplosTraversal(\SplObjectStorage $splos) {
$this->walking->detach($splos);
}
}
\ No newline at end of file
<?php
namespace Gliph\Graph;
use Gliph\Algorithm\ConnectedComponent;
use Gliph\Exception\NonexistentVertexException;
use Gliph\Exception\RuntimeException;
use Gliph\Traversal\DepthFirst;
use Gliph\Visitor\DepthFirstToposortVisitor;
class DirectedAdjacencyList extends AdjacencyList implements MutableDirectedGraph {
/**
* {@inheritdoc}
*/
public function addDirectedEdge($tail, $head) {
$this->addVertex($tail)->addVertex($head);
if (!$this->vertices[$tail]->contains($head)) {
$this->size++;
}
$this->vertices[$tail]->attach($head);
}
/**
* {@inheritdoc}
*/
public function removeVertex($vertex) {
if (!$this->hasVertex($vertex)) {
throw new NonexistentVertexException('Vertex is not in the graph, it cannot be removed.', E_WARNING);
}
$this->eachVertex(function($v, $outgoing) use ($vertex) {
if ($outgoing->contains($vertex)) {
$outgoing->detach($vertex);
}
});
unset($this->vertices[$vertex]);
}
/**
* {@inheritdoc}
*/
public function removeEdge($tail, $head) {
$this->vertices[$tail]->detach($head);
}
/**
* {@inheritdoc}
*/
public function eachEdge($callback) {
$edges = array();
$that = $this;
$this->fev(function ($from, $outgoing) use (&$edges, $that) {
$set = $that->_getTraversableSplos($outgoing);
foreach ($set as $to) {
$edges[] = array($from, $to);
}
$that->_cleanupSplosTraversal($set);
});
foreach ($edges as $edge) {
call_user_func($callback, $edge);
}
}
/**
* {@inheritdoc}
*/
public function transpose() {
$graph = new self();
$this->eachEdge(function($edge) use (&$graph) {
$graph->addDirectedEdge($edge[1], $edge[0]);
});
return $graph;
}
/**
* {@inheritdoc}
*/
public function isAcyclic() {
// The DepthFirstToposortVisitor throws an exception on cycles.