/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2010, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* -------------------------
* RankingPathElementList.java
* -------------------------
* (C) Copyright 2007-2010, by France Telecom
*
* Original Author: Guillaume Boulmier and Contributors.
* Contributor(s): John V. Sichi
*
* $Id$
*
* Changes
* -------
* 05-Jun-2007 : Initial revision (GB);
* 05-Jul-2007 : Added support for generics (JVS);
* 06-Dec-2010 : Bugfixes (GB);
*
*/
package org.jgrapht.alg;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.graph.*;
/**
* List of simple paths in increasing order of weight.
*
* @author Guillaume Boulmier
* @since July 5, 2007
*/
final class RankingPathElementList<V, E>
extends AbstractPathElementList<V, E, RankingPathElement<V, E>>
{
/**
* Vertex that paths of the list must not disconnect.
*/
private V guardVertexToNotDisconnect = null;
private Map<RankingPathElement<V, E>, Boolean> path2disconnect =
new HashMap<RankingPathElement<V, E>, Boolean>();
/**
* Creates a list with an empty path. The list size is 1.
*
* @param maxSize max number of paths the list is able to store.
*/
RankingPathElementList(
Graph<V, E> graph,
int maxSize,
RankingPathElement<V, E> pathElement)
{
super(graph, maxSize, pathElement);
}
/**
* Creates paths obtained by concatenating the specified edge to the
* specified paths.
*
* @param prevPathElementList paths, list of <code>
* RankingPathElement</code>.
* @param edge edge reaching the end vertex of the created paths.
* @param maxSize maximum number of paths the list is able to store.
*/
RankingPathElementList(
Graph<V, E> graph,
int maxSize,
RankingPathElementList<V, E> elementList,
E edge)
{
this(graph, maxSize, elementList, edge, null);
assert (!this.pathElements.isEmpty());
}
/**
* Creates paths obtained by concatenating the specified edge to the
* specified paths.
*
* @param prevPathElementList paths, list of <code>
* RankingPathElement</code>.
* @param edge edge reaching the end vertex of the created paths.
* @param maxSize maximum number of paths the list is able to store.
*/
RankingPathElementList(
Graph<V, E> graph,
int maxSize,
RankingPathElementList<V, E> elementList,
E edge,
V guardVertexToNotDisconnect)
{
super(graph, maxSize, elementList, edge);
this.guardVertexToNotDisconnect = guardVertexToNotDisconnect;
// loop over the path elements in increasing order of weight.
for (int i = 0; (i < elementList.size()) && (size() < maxSize); i++) {
RankingPathElement<V, E> prevPathElement = elementList.get(i);
if (isNotValidPath(prevPathElement, edge)) {
// go to the next path element in the loop
continue;
}
double weight = calculatePathWeight(prevPathElement, edge);
RankingPathElement<V, E> newPathElement =
new RankingPathElement<V, E>(
this.graph,
prevPathElement,
edge,
weight);
// the new path is inserted at the end of the list.
this.pathElements.add(newPathElement);
}
}
/**
* Creates an empty list. The list size is 0.
*
* @param maxSize max number of paths the list is able to store.
*/
RankingPathElementList(Graph<V, E> graph, int maxSize, V vertex)
{
super(graph, maxSize, vertex);
}
/**
* <p>Adds paths in the list at vertex y. Candidate paths are obtained by
* concatenating the specified edge (v->y) to the paths <code>
* elementList</code> at vertex v.</p>
*
* Complexity =
*
* <ul>
* <li>w/o guard-vertex: O(<code>k*np</code>) where <code>k</code> is the
* max size limit of the list and <code>np</code> is the maximum number of
* vertices in the paths stored in the list</li>
* <li>with guard-vertex: O(<code>k*(m+n)</code>) where <code>k</code> is
* the max size limit of the list, <code>m</code> is the number of edges of
* the graph and <code>n</code> is the number of vertices of the graph,
* O(<code>m+n</code>) being the complexity of the <code>
* ConnectivityInspector</code> to check whether a path exists towards the
* guard-vertex</li>
* </ul>
*
* @param elementList list of paths at vertex v.
* @param edge edge (v->y).
*
* @return <code>true</code> if at least one path has been added in the
* list, <code>false</code> otherwise.
*/
public boolean addPathElements(
RankingPathElementList<V, E> elementList,
E edge)
{
assert (this.vertex.equals(
Graphs.getOppositeVertex(
this.graph,
edge,
elementList.getVertex())));
boolean pathAdded = false;
// loop over the paths elements of the list at vertex v.
for (
int vIndex = 0, yIndex = 0;
vIndex < elementList.size();
vIndex++)
{
RankingPathElement<V, E> prevPathElement = elementList.get(vIndex);
if (isNotValidPath(prevPathElement, edge)) {
// checks if path is simple and if guard-vertex is not
// disconnected.
continue;
}
double newPathWeight = calculatePathWeight(prevPathElement, edge);
RankingPathElement<V, E> newPathElement =
new RankingPathElement<V, E>(
this.graph,
prevPathElement,
edge,
newPathWeight);
// loop over the paths of the list at vertex y from yIndex to the
// end.
RankingPathElement<V, E> yPathElement = null;
for (; yIndex < size(); yIndex++) {
yPathElement = get(yIndex);
// case when the new path is shorter than the path Py stored at
// index y
if (newPathWeight < yPathElement.getWeight()) {
this.pathElements.add(yIndex, newPathElement);
pathAdded = true;
// ensures max size limit is not exceeded.
if (size() > this.maxSize) {
this.pathElements.remove(this.maxSize);
}
break;
}
// case when the new path is of the same length as the path Py
// stored at index y
if (newPathWeight == yPathElement.getWeight()) {
this.pathElements.add(yIndex + 1, newPathElement);
pathAdded = true;
// ensures max size limit is not exceeded.
if (size() > this.maxSize) {
this.pathElements.remove(this.maxSize);
}
break;
}
}
// case when the new path is longer than the longest path in the
// list (Py stored at the last index y)
if (newPathWeight > yPathElement.getWeight()) {
// ensures max size limit is not exceeded.
if (size() < this.maxSize) {
// the new path is inserted at the end of the list.
this.pathElements.add(newPathElement);
pathAdded = true;
} else {
// max size limit is reached -> end of the loop over the
// paths elements of the list at vertex v.
break;
}
}
}
return pathAdded;
}
/**
* @return list of <code>RankingPathElement</code>.
*/
List<RankingPathElement<V, E>> getPathElements()
{
return this.pathElements;
}
/**
* Costs taken into account are the weights stored in <code>Edge</code>
* objects.
*
* @param pathElement
* @param edge the edge via which the vertex was encountered.
*
* @return the cost obtained by concatenation.
*
* @see Graph#getEdgeWeight(E)
*/
private double calculatePathWeight(
RankingPathElement<V, E> pathElement,
E edge)
{
double pathWeight = this.graph.getEdgeWeight(edge);
// otherwise it's the start vertex.
if ((pathElement.getPrevEdge() != null)) {
pathWeight += pathElement.getWeight();
}
return pathWeight;
}
/**
* Ensures that paths of the list do not disconnect the guard-vertex.
*
* @return <code>true</code> if the specified path element disconnects the
* guard-vertex, <code>false</code> otherwise.
*/
private boolean isGuardVertexDisconnected(
RankingPathElement<V, E> prevPathElement)
{
if (this.guardVertexToNotDisconnect == null) {
return false;
}
if (this.path2disconnect.containsKey(prevPathElement)) {
return this.path2disconnect.get(prevPathElement);
}
ConnectivityInspector<V, E> connectivityInspector;
MaskFunctor<V, E> connectivityMask;
if (this.graph instanceof DirectedGraph<?, ?>) {
connectivityMask = new PathMask<V, E>(prevPathElement);
DirectedMaskSubgraph<V, E> connectivityGraph =
new DirectedMaskSubgraph<V, E>(
(DirectedGraph<V, E>) this.graph,
connectivityMask);
connectivityInspector =
new ConnectivityInspector<V, E>(
connectivityGraph);
} else {
connectivityMask = new PathMask<V, E>(prevPathElement);
UndirectedMaskSubgraph<V, E> connectivityGraph =
new UndirectedMaskSubgraph<V, E>(
(UndirectedGraph<V, E>) this.graph,
connectivityMask);
connectivityInspector =
new ConnectivityInspector<V, E>(
connectivityGraph);
}
if (connectivityMask.isVertexMasked(this.guardVertexToNotDisconnect)) {
// the guard-vertex was already in the path element -> invalid path
this.path2disconnect.put(prevPathElement, true);
return true;
}
if (!connectivityInspector.pathExists(
this.vertex,
this.guardVertexToNotDisconnect))
{
this.path2disconnect.put(prevPathElement, true);
return true;
}
this.path2disconnect.put(prevPathElement, false);
return false;
}
private boolean isNotValidPath(
RankingPathElement<V, E> prevPathElement,
E edge)
{
return !isSimplePath(prevPathElement, edge)
|| isGuardVertexDisconnected(prevPathElement);
}
/**
* Ensures that paths of the list are simple (check that the vertex was not
* already in the path element).
*
* @param prevPathElement
* @param edge
*
* @return <code>true</code> if the resulting path (obtained by
* concatenating the specified edge to the specified path) is simple, <code>
* false</code> otherwise.
*/
private boolean isSimplePath(
RankingPathElement<V, E> prevPathElement,
E edge)
{
V endVertex =
Graphs.getOppositeVertex(
this.graph,
edge,
prevPathElement.getVertex());
assert (endVertex.equals(this.vertex));
RankingPathElement<V, E> pathElementToTest = prevPathElement;
do {
if (pathElementToTest.getVertex().equals(endVertex)) {
return false;
} else {
pathElementToTest = pathElementToTest.getPrevPathElement();
}
} while (pathElementToTest != null);
return true;
}
private static class PathMask<V, E>
implements MaskFunctor<V, E>
{
private Set<E> maskedEdges;
private Set<V> maskedVertices;
/**
* Creates a mask for all the edges and the vertices of the path
* (including the 2 extremity vertices).
*
* @param pathElement
*/
PathMask(RankingPathElement<V, E> pathElement)
{
this.maskedEdges = new HashSet<E>();
this.maskedVertices = new HashSet<V>();
while (pathElement.getPrevEdge() != null) {
this.maskedEdges.add(pathElement.getPrevEdge());
this.maskedVertices.add(pathElement.getVertex());
pathElement = pathElement.getPrevPathElement();
}
this.maskedVertices.add(pathElement.getVertex());
}
// implement MaskFunctor
public boolean isEdgeMasked(E edge)
{
return this.maskedEdges.contains(edge);
}
// implement MaskFunctor
public boolean isVertexMasked(V vertex)
{
return this.maskedVertices.contains(vertex);
}
}
}
// End RankingPathElementList.java