Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Set\Vertices and Set\Edges classes #48

Merged
merged 8 commits into from
Sep 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@ you spot any mistakes.

## 0.7.0 (2013-xx-xx)

* Feature: Add new `Set\Vertices` and `Set\Edges` classes that handle common
operations on a Set of multiple `Vertex` and `Edge` instances respectively.
([#48](https://github.com/clue/graph/issues/48))

* BC break: Move operations and their corresponding constants concerning Sets
to their corresponding Sets:

| Old name | New name |
|---|---|
| `Edge\Base::getFirst()` | `Set\Edges::getEdgeOrder()` |
| `Edge\Base::getAll()` | `Set\Edges::getEdgesOrder()` |
| `Edge\Base::ORDER_*` | `Set\Edges::ORDER_* |
|---|---|
| `Vertex::getFirst()` | `Set\Vertices::getVertexOrder()` |
| `Vertex::getAll()` | `Set\Vertices::getVerticesOrder()` |
| `Vertex::ORDER_` | `Set\Vertices::ORDER_*` |

* BC break: Each `getVertices*()` and `getEdges*()` method now returns a `Set`
instead of a primitive array of instances. *Most* of the time this should
work without changing your code, because each `Set` implements an `Iterator`
interface and can easily be iterated using `foreach`. However, using a `Set`
instead of a plain array differs when checking its boolean value or
comparing two Sets. I.e. if you happen to want to check if an `Set` is empty,
you now have to use the more explicit syntax `$set->isEmpty()`.

* BC break: `Vertex::getVertices()`, `Vertex::getVerticesEdgeTo()` and
`Vertex::getVerticesEdgeFrom()` now return a `Set\Vertices` instance that
may contain duplicate vertices if parallel (multiple) edges exist. Previously
there was no easy way to detect this situation - this is now the default. If
you also want to get unique / distinct `Vertex` instances, use
`Vertex::getVertices()->getVerticesDistinct()` where applicable.

* BC break: Remove all occurances of `getVerticesId()`, use
`getVertices()->getIds()` instead.

* BC break: Merge `Cycle` into `Walk` ([#61](https://github.com/clue/graph/issues/61)).
As such, its static factory methods had to be renamed. Update your references if applicable:

Expand Down Expand Up @@ -33,6 +68,7 @@ Vertex ([#62](https://github.com/clue/graph/issues/62))
([#62](https://github.com/clue/graph/issues/62))
* Feature: Add `Algorithm\ShortestPath::hasVertex(Vertex $vertex)` to check whether
a path to the given Vertex exists ([#62](https://github.com/clue/graph/issues/62)).
* Feature: Add `Walk::factoryFromVertices()` ([#64](https://github.com/clue/graph/issues/64)).
* Fix: Checking `Walk::isValid()` ([#61](https://github.com/clue/graph/issues/61))
* Fix: Missing import prevented
`Algorithm\ShortestPath\MooreBellmanFord::getCycleNegative()` from actually
Expand Down
3 changes: 2 additions & 1 deletion lib/Fhaculty/Graph/Algorithm/MaximumMatching/Flow.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Fhaculty\Graph\Algorithm\MaxFlow\EdmondsKarp as MaxFlowEdmondsKarp;
use Fhaculty\Graph\Algorithm\Groups;
use Fhaculty\Graph\Exception;
use Fhaculty\Graph\Set\Edges;

class Flow extends Base
{
Expand Down Expand Up @@ -81,6 +82,6 @@ public function getEdges()
}
}

return $returnEdges;
return new Edges($returnEdges);
}
}
68 changes: 47 additions & 21 deletions lib/Fhaculty/Graph/Walk.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,42 @@ public static function factoryFromEdges($edges, Vertex $startVertex)
return new self($vertices, $edges);
}

/**
* create new walk instance between given set of Vertices / array of Vertex instances
*
* @param Vertices|Vertex[] $vertices
* @param int|null $by
* @param boolean $desc
* @return Walk
* @throws UnderflowException if no vertices were given
* @see Edges::getEdgeOrder() for parameters $by and $desc
*/
public static function factoryFromVertices($vertices, $by = null, $desc = false)
{
$edges = array();
$first = NULL;
$last = NULL;
foreach ($vertices as $vertex) {
// skip first vertex as last is unknown
if ($first === NULL) {
$first = $vertex;
} else {
// pick edge between last vertex and this vertex
if ($by === null) {
$edges []= $last->getEdgesTo($vertex)->getEdgeFirst();
} else {
$edges []= $last->getEdgesTo($vertex)->getEdgeOrder($by, $desc);
}
}
$last = $vertex;
}
if ($last === NULL) {
throw new UnderflowException('No vertices given');
}

return new self($vertices, $edges);
}

/**
* create new cycle instance from given predecessor map
*
Expand Down Expand Up @@ -102,41 +138,31 @@ public static function factoryCycleFromPredecessorMap($predecessors, $vertex, $b
* @return Walk
* @throws UnderflowException if no vertices were given
* @see Edges::getEdgeOrder() for parameters $by and $desc
* @uses self::factoryFromVertices()
*/
public static function factoryCycleFromVertices($vertices, $by = null, $desc = false)
{
$edges = array();
$first = NULL;
$last = NULL;
foreach ($vertices as $vertex) {
// skip first vertex as last is unknown
if ($first === NULL) {
$first = $vertex;
} else {
// pick edge between last vertex and this vertex
if ($by === null) {
$edges []= $last->getEdgesTo($vertex)->getEdgeFirst();
} else {
$edges []= $last->getEdgesTo($vertex)->getEdgeOrder($by, $desc);
}
}
$last = $vertex;
}
if ($last === NULL) {
throw new UnderflowException('No vertices given');
}
$cycle = self::factoryFromVertices($vertices, $by, $desc);

$first = $cycle->getVertexSource();
$last = $cycle->getVertexTarget();

// additional edge from last vertex to first vertex
if ($last !== $first) {
$vertices = $cycle->getVertices()->getVector();
$edges = $cycle->getEdges()->getVector();

if ($by === null) {
$edges []= $last->getEdgesTo($first)->getEdgeFirst();
} else {
$edges []= $last->getEdgesTo($first)->getEdgeOrder($by, $desc);
}
$vertices []= $first;

$cycle = new self($vertices, $edges);
}

return new self($vertices, $edges);
return $cycle;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function testSingleEdge()
// correct number of edges
$this->assertEquals(1, $alg->getNumberOfMatches());
// actual edge instance returned
$this->assertEquals(array($edge), $alg->getEdges());
$this->assertEquals(array($edge), $alg->getEdges()->getVector());

// check
$flowgraph = $alg->createGraph();
Expand Down
8 changes: 8 additions & 0 deletions tests/Fhaculty/Graph/WalkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ public function testWalkWithinGraph()

$this->assertGraphEquals($graphExpected, $walk->createGraph());

// construct same partial walk "1 -- 2"
$walkVertices = Walk::factoryFromVertices(array($v1, $v2));

$this->assertEquals(2, $walkVertices->getNumberOfVertices());
$this->assertEquals(1, $walkVertices->getNumberOfEdges());

$this->assertGraphEquals($graphExpected, $walkVertices->createGraph());

return $walk;
}

Expand Down