/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb.algorithms;
import java.util.*;
import org.hypergraphdb.*;
import org.hypergraphdb.atom.HGAtomSet;
import org.hypergraphdb.handle.HGLiveHandle;
import org.hypergraphdb.util.Mapping;
import org.hypergraphdb.util.Pair;
/**
*
* <p>
* A collection of classical graph algorithms implemented within the HyperGraphDB framework.
* </p>
*
* @author Borislav Iordanov
*
*/
public class GraphClassics
{
/**
*
* <p>
* Detect whether a sub-graph has cycles. Links are specified through an
* <code>HGALGenerator</code> as usual.
* </p>
*
* @param root The starting point for sub-graph exploration.
* @param adjencyGenerator Generator for atoms adjacent to the current node being examined.
* @return <code>true</code> if the sub-graph has cycles and <code>false</code> otherwise.
*/
public static boolean hasCycles(final HGHandle root, final HGALGenerator adjencyGenerator)
{
HGAtomSet visited = new HGAtomSet();
Queue<HGHandle> to_explore = new LinkedList<HGHandle>();
to_explore.add(root);
while (!to_explore.isEmpty())
{
HGHandle next = to_explore.remove();
visited.add(next);
HGSearchResult<Pair<HGHandle, HGHandle>> rs = adjencyGenerator.generate(next);
try
{
while (rs.hasNext())
{
Pair<HGHandle, HGHandle> x = rs.next();
if (visited.contains(x.getSecond()))
return true;
to_explore.add(x.getSecond());
}
}
finally
{
rs.close();
}
}
return false;
}
/**
* <p>
* Simplified interface to Dijkstra's algorithm - calls the full version with
* the remaining arguments set to <code>null</code>.
* </p>
*
* @param start
* @param goal
* @param adjencyGenerator
* @return The number of edges b/w <code>start</code> and <code>goal</code> or
* <code>null</code> if they are not connected.
*/
public static Double dijkstra(final HGHandle start, final HGHandle goal, final HGALGenerator adjencyGenerator)
{
return dijkstra(start, goal, adjencyGenerator, null, null, null);
}
/**
* <p>
* Implements Dijkstra's algorithm for finding the shortest path between two
* nodes (i.e. atoms). The method returns the distance between <code>start</code>
* and <code>goal</code> or <code>null</code> if the two atoms are not connected.
* </p>
*
* <p>
* The method allows you to optionally pass your own main data structures
* for use in the algorithm's implementation. If you pass <code>null</code> as the
* value of a data structure, the method will create and use its own. Thus,
* if you care about the actual paths computed and/or all distances between
* nodes on those paths, you should provide your own <code>distanceMatrix</code>
* and <code>predecessorMatrix</code>.
* </p>
*
* <p>
* The <code>weight</code> mapping argument represents a function that computes
* the weight of a given link. If you pass <code>null</code>, a weight of 1
* will be used for all links. Note that this mapping cannot return negative
* values. Dijkstra's algorithms assumes non-negative weights. If the weights
* of your graph can be negative, use the <code>bellman_ford</code> instead.
* </p>
*
* @param start
* @param goal
* @param adjacencyGenerator
* @param weight The function that computes that weight of a link for the purposes
* of measuring the distance between nodes. If <code>null</code>, the constant
* function 1 will be used.
* @param distanceMatrix The data structure holding the computed distances between
* the <code>start</code> atom and all other atoms encountered during the search. Only
* <code>put</code> and <code>get</code> are used so you can provide an implementation
* that only implements those two methods. If <code>null</code> is passed, a new
* temporary <code>HashMap</code> will be instantiated and used throughout the search.
* @param predecessorMatrix A map storing the predecessor atoms computed during
* the search. Again, only <code>put</code> and <code>get</code> are used here. If
* <code>null</code>, the predecessor will not be stored anywhere.
* @return The distance between <code>start</code> and <code>goal</code> or
* <code>null</code> if <code>start</code> is unreachable from <code>goal</code>.
*/
public static Double dijkstra(final HGHandle start,
final HGHandle goal,
final HGALGenerator adjacencyGenerator,
Mapping<HGHandle, Double> weight,
Map<HGHandle, Double> distanceMatrix,
Map<HGHandle, HGHandle> predecessorMatrix)
{
final Map<HGHandle, Double> dm =
distanceMatrix == null ? new HashMap<HGHandle, Double>() : distanceMatrix;
dm.put(start, 0.0);
Comparator<HGHandle> comp = new Comparator<HGHandle>()
{
private int compareHandles(HGHandle left, HGHandle right)
{
HGPersistentHandle x = left instanceof HGPersistentHandle ?
(HGPersistentHandle)left : ((HGLiveHandle)left).getPersistent();
HGPersistentHandle y = right instanceof HGPersistentHandle ?
(HGPersistentHandle)right : ((HGLiveHandle)right).getPersistent();
return x.compareTo(y);
}
public int compare(HGHandle left, HGHandle right)
{
Double l = dm.get(left);
Double r = dm.get(right);
if (l == null)
if (r == null)
return compareHandles(left, right);
else
return 1;
else if (r == null)
return -1;
else
{
int c = l.compareTo(r);
if (c == 0)
c = compareHandles(left, right);
return c;
}
}
};
if (weight == null)
weight = new Mapping<HGHandle, Double>() { public Double eval(HGHandle link) { return 1.0; } };
HGAtomSet settled = new HGAtomSet();
TreeSet<HGHandle> unsettled = new TreeSet<HGHandle>(comp);
unsettled.add(start);
while (!unsettled.isEmpty())
{
HGHandle a = unsettled.first();
unsettled.remove(a);
if (a.equals(goal))
return dm.get(goal);
settled.add(a);
HGSearchResult<Pair<HGHandle, HGHandle>> neighbors = adjacencyGenerator.generate(a);
double weightCurrent = dm.get(a).doubleValue();
while (neighbors.hasNext())
{
Pair<HGHandle, HGHandle> n = neighbors.next();
if (settled.contains(n.getSecond()))
continue;
Double weightN = dm.get(n.getSecond());
Double weightAN = weight.eval(n.getFirst());
if (weightN == null)
{
dm.put(n.getSecond(), weightCurrent + weightAN);
unsettled.add(n.getSecond());
if (predecessorMatrix != null)
predecessorMatrix.put(n.getSecond(), a);
}
else if (weightN > weightCurrent + weightAN)
{
// new distance found for n, re-insert at appropriate position
unsettled.remove(n.getSecond());
dm.put(n.getSecond(), weightCurrent + weightAN);
unsettled.add(n.getSecond());
if (predecessorMatrix != null)
predecessorMatrix.put(n.getSecond(), a);
}
}
neighbors.close();
}
return null;
}
public void bellman_ford()
{
}
public void a_star()
{
}
public void johnson()
{
}
public void floyd_warshall()
{
}
public void prim(HGHandle start,
final HGALGenerator adjencyGenerator,
Mapping<HGHandle, Double> weight,
Map<HGHandle, HGHandle> parentMatrix)
{
}
public void kruskall(Iterator<HGHandle> links,
Mapping<HGHandle, Double> weight,
Map<HGHandle, HGHandle> parentMatrix)
{
}
}