/*
* @(#)VersantGraph.java 1.0 Jul 27, 2011
*
* Copyright 2000-2011 ETH Zurich. All Rights Reserved.
*
* This software is the proprietary information of ETH Zurich.
* Use is subject to license terms.
*
* @(#) $Id: VersantGraph.java 2019 2011-10-14 11:28:40Z D\sleone $
*/
package org.zoodb.test.jdo.sna;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.zoodb.api.impl.ZooPC;
/**
* Represents a graph consisting of a set of nodes and a set of edges and
* redundant information about Predecessor matrix via row indices.
*
* @author Ilija Bogunovic <ilijab@student.ethz.ch>
* @author Darijan Jankovic <jdarijan@student.ethz.ch>
* @author Stefania leone <leone@inf.ethz.ch>
* @version 1.0
*/
public class VersantGraph extends ZooPC {
/**
* Constant for the predecessor matrix
*/
public static final int NEIGHBOUR_NODE = -1;
/**
* Constant for the predecessor matrix
*/
public static final int UNREACHABLE_NODE = -2;
/**
* Constant for the predecessor matrix
*/
public static final int DISTANCETOSELF = 0;
/**
* Vertex states for bridge-finding algorithm
*/
enum VertexState {
visited, notVisited, progress
};
/**
* The name of this graph.
*/
private final String name;
/**
* Map of nodes objects that form this graph.
*/
private final FastIdList<VersantNode> nodes;
/**
* A set of edge objects that connect all the nodes inside this graph.
*/
private final HashSet<VersantEdge> edges;
/**
* private constructor for JDO.
*/
@SuppressWarnings("unused")
private VersantGraph() {
this.name = null;
this.nodes = null;
this.edges = null;
}
/**
* Constructs a default Graph object with default name and empty collections.
*/
public VersantGraph(final String name) {
this.name = name;
this.nodes = new FastIdList<VersantNode>();
this.edges = new HashSet<VersantEdge>();
}
/**
* Returns the name of a graph.
*
* @return name of the graph.
*/
public String getName() {
zooActivateRead();
return this.name;
}
public Collection<VersantNode> getNodes() {
zooActivateRead();
return null;
}
/**
* Returns node with id <code>nodeId</code>.
*
* @param id
* id of a node in the graph.
* @return node of a graph.
*/
//TODO callee (LONG)
public VersantNode getNode(final Long id) {
zooActivateRead();
return this.getNodeInternal(id.intValue());
}
private VersantNode getNodeInternal(final int id) {
VersantNode node = null;
try {
node = this.nodes.get(id);
} catch (final Exception ex) {
System.out.println("Node with " + id + " id does not exist.");
}
return node;
}
/**
* Returns an edge between a source node with id <code>sourceId</code> and a
* target node with id <code>targetId</code>.
*
* @param sourceId
* id of a source node in the graph.
* @param targetId
* id of a target node in the graph.
* @return an edge between given nodes if exists.
*/
public VersantEdge getEdge(final Long sourceId, final Long targetId) {
zooActivateRead();
return this.getEdgeInternal(sourceId.intValue(), targetId.intValue());
}
private VersantEdge getEdgeInternal(final int sourceNodeIndex,
final int targetNodeIndex) {
for (final VersantEdge e : this.getEdges()) {
if (e.getSource().getBasicId() == sourceNodeIndex
&& e.getTarget().getBasicId() == targetNodeIndex) {
return e;
}
}
return null;
}
/**
* Returns a row index with id <code>rowIndexId</code>.
*
* @param index
* id of a row index in the predecessor matrix.
* @return row index of the predecessor matrix.
*/
public Map<Integer, EdgePropertiesImpl> getRowIndex(final int index) {
zooActivateRead();
return this.nodes.get(index).getRowIndex();
}
/**
* Returns a map containing all nodes.
*
* @return a map containing all nodes.
*/
public Map<Integer, VersantNode> getNodeMap() {
zooActivateRead();
return this.nodes.asMap();
}
/**
* Returns a set containing all edges.
*
* @return a set containing all edges.
*/
public HashSet<VersantEdge> getEdges() {
zooActivateRead();
return this.edges;
}
/**
* Returns a map containing all row indices.
*
* @return a map containing all row indices.
*/
// @Override
// public Map<Object, RowIndex> getRowIndices() {
// return this.rowIndices;
// }
/**
* Inserts the specified element in the map of all nodes.
*
* @param node
* node to be inserted.
*/
public void insertNode(final VersantNode node) {
zooActivateWrite();
this.nodes.put(((VersantNode) node).getBasicId(), (VersantNode) node);
}
/**
* Inserts the specified element in the set of all edges.
*
* @param edge
* edge to be inserted.
*/
public void insertEdge(final VersantEdge edge) {
zooActivateWrite();
this.edges.add((VersantEdge) edge);
}
/**
* Removes the specified element from the set of all edges.
*
* @param edge
* edge to be removed.
*/
public void removeEdge(final VersantEdge edge) {
zooActivateWrite();
this.edges.remove(edge);
}
/**
* Returns the degree of the given node. The degree is defined as the number
* of ties of a node to other nodes in the graph.
*
* @param nodeId
* id of a node in the graph.
* @return degree of node with id <code>nodeId</code>.
*/
public int nodeDegree(final int nodeId) {
zooActivateRead();
return this.getNodeInternal(nodeId).getNodeDegree();
}
/**
* Checks whether there is a path in the given graph between the node with id
* <code>sourceNodeId</code> and the node with id <code>targetNodeId</code>.
*
* @param sourceNodeId
* id of the source node.
* @param targetNodeId
* id of the target node.
* @return <code>true</code> if the given nodes are connected,
* <code>false</code> otherwise.
*/
public boolean connected(final int sourceNodeId, final int targetNodeId) {
zooActivateRead();
final VersantNode node = this.nodes.get(sourceNodeId);
for (final VersantEdge edge : node.getEdges()) {
if (edge.getTarget().getBasicId() == targetNodeId) {
return true;
}
}
return false;
}
public double betweennessCentralityUndirected(final int nodeId) {
zooActivateRead();
return FloydWarshall.betweennessCentralityUndirected(nodeId, this.nodes.asMap());
// double sum = 0.0;
//
// for (int i = 1; i < this.nodes.size() + 1; i++) {
// for (int j = i + 1; j < this.nodes.size() + 1; j++) {
//
// if (i != nodeId && j != nodeId) {
// final VersantNode n1 = this.nodes.get(i);
//
// final EdgePropertiesImpl ep1 = n1.getRowIndex().get(j);
// final EdgePropertiesImpl ep2 = n1.getRowIndex().get(nodeId);
//
// if (ep1.getDistance() > ep2.getDistance()) {
// final VersantNode n3 = this.nodes.get(nodeId);
// final EdgePropertiesImpl ep3 = n3.getRowIndex().get(j);
//
// if (ep2.getDistance() + ep3.getDistance() == ep1
// .getDistance()) {
// sum += (double) (ep2.getPathCount() * ep3.getPathCount())
// / ep1.getPathCount();
// }
// }
// }
// }
// }
// return 2 * sum;
}
public double betweennessCentralityDirected(final int nodeId) {
zooActivateRead();
return FloydWarshall.betweennessCentralityDirected(nodeId, this.nodes.asMap());
// Double sum = 0.0;
//
// for (int i = 1; i < this.nodes.size() + 1; i++) {
// for (int j = 1; j < this.nodes.size() + 1; j++) {
//
// if (i != j && i != nodeId && j != nodeId) {
// final VersantNode n1 = this.nodes.get(i);
//
// final EdgePropertiesImpl ep1 = n1.getRowIndex().get(j);
// final EdgePropertiesImpl ep2 = n1.getRowIndex().get(nodeId);
//
// if (ep1.getDistance() > ep2.getDistance()) {
// final VersantNode n3 = this.nodes.get(nodeId);
// final EdgePropertiesImpl ep3 = n3.getRowIndex().get(j);
//
// if (ep2.getDistance() + ep3.getDistance() == ep1
// .getDistance()) {
// sum += (double) (ep2.getPathCount() * ep3.getPathCount())
// / ep1.getPathCount();
// }
// }
// }
// }
// }
// return sum;
}
/**
* Computes closeness centrality on a unweighted graph for the node with id
* <code>nodeId</code>.
*
* @param nodeId
* id of a node in the graph.
* @return closeness centrality of the given node.
*/
public double closenessCentrality(final int nodeId) {
zooActivateRead();
final VersantNode source = this.getNodeInternal(nodeId);
final Map<Integer, EdgePropertiesImpl> props = source.getRowIndex();
double sum = 0.0;
double d = 0.0;
boolean removeMe = false;
for (final EdgePropertiesImpl current : props.values()) {
if (current == null) {
if (removeMe == true) {
throw new RuntimeException("Only the first element should be null!");
}
removeMe = true;
continue;
}
d = current.getDistance();
if (d != VersantGraph.UNREACHABLE_NODE) {
sum += d;
}
}
return 1 / sum;
}
/**
* Computes and returns the average path and diameter of the given graph.
*
* @return average path and diameter.
*/
public double averagePathAndDiameter() {
zooActivateRead();
double current = 0.0;
double d = 0.0;
for (int i = 1; i < this.nodes.size() + 1; i++) {
final VersantNode srcNode = this.nodes.get(i);
boolean removeMe = false;
for (final EdgePropertiesImpl props : srcNode.getRowIndex().values()) {
if (props == null) {
if (removeMe == true) {
throw new RuntimeException("Only the first element should be null!");
}
removeMe = true;
continue;
}
current = props.getDistance();
if (d != VersantGraph.UNREACHABLE_NODE) {
if (current > d) {
d = current;
}
}
}
}
return d;
}
/**
* Finds and returns all ids of nodes that are bridges in the given graph.
*
* @return list of all edges representing bridges.
*/
public ArrayList<VersantEdge> findBridges() {
final FindBridgesAlgorithm fba = new FindBridgesAlgorithm();
return fba.findBridges();
}
/**
* Class designed exclusively to find bridges for the given graph.
*/
class FindBridgesAlgorithm {
private final ArrayList<ArrayList<Integer>> dfsTree = new ArrayList<ArrayList<Integer>>();
private final ArrayList<HashMap<Integer, ArrayList<Integer>>> backEdges = new ArrayList<HashMap<Integer, ArrayList<Integer>>>();
private final ArrayList<Integer> dfsTreeTemp = new ArrayList<Integer>();
private final HashMap<Integer, ArrayList<Integer>> backEdgesTemp = new HashMap<Integer, ArrayList<Integer>>();
/**
* Bridge finding algorithm.
*
* @return List of edges which are considered bridges.
*/
public ArrayList<VersantEdge> findBridges() {
zooActivateRead();
final ArrayList<VersantEdge> bridges = new ArrayList<VersantEdge>();
final ArrayList<Integer> nd = new ArrayList<Integer>();
final ArrayList<Integer> l = new ArrayList<Integer>();
final ArrayList<Integer> h = new ArrayList<Integer>();
this.dfs();
int lt;
int ht;
int noDesc;
final int nodesCount = VersantGraph.this.nodes.size();
for (int i = 0; i < nodesCount + 1; i++) {
nd.add(0);
l.add(0);
h.add(0);
}
for (int j = 0; j < this.dfsTree.size(); j++) {
final ArrayList<Integer> nodesTree = this.dfsTree.get(j);
final HashMap<Integer, ArrayList<Integer>> backEdges = this.backEdges.get(j);
for (final Integer i : nodesTree) {
final VersantNode nI = VersantGraph.this.getNodeInternal(i);
noDesc = 1;
lt = i;
ht = i;
int u;
int lv;
int hv;
int p;
for (final VersantEdge neighbourEdge : nI.getEdges()) {
u = neighbourEdge.getTarget().getBasicId();
lv = l.get(u);
hv = h.get(u);
if (u > i) {
if (lt > lv) {
lt = lv;
}
if (ht < hv) {
ht = hv;
}
noDesc += nd.get(u);
//TODO i i.o. Integer.valueOf ??
} else if (backEdges.get(i) != null
&& backEdges.get(i).contains(u)) {
if (lt > u) {
lt = u;
}
if (ht < u) {
ht = u;
}
}
}
l.set(i, lt);
h.set(i, ht);
nd.set(i, noDesc);
for (final VersantEdge currentEdge : nI.getEdges()) {
p = currentEdge.getTarget().getBasicId();
if (p > i) {
if (l.get(p) >= p && h.get(p) < p + nd.get(p)) {
bridges.add(currentEdge);
}
}
}
}
}
return bridges;
}
/**
* Depth-first search algorithm which makes a spanning tree of a graph.
*/
private void dfs() {
zooActivateRead();
final int graphSize = VersantGraph.this.nodes.size();
final VertexState[] state = new VertexState[graphSize];
for (int i = 0; i < graphSize; i++) {
state[i] = VertexState.notVisited;
}
for (int i = 1; i <= graphSize; i++) {
if (state[i - 1] == VertexState.notVisited) {
final VersantNode srcNode = VersantGraph.this.getNodeInternal(i);
this.runDFS(srcNode.getBasicId(), state);
this.dfsTree.add(new ArrayList<Integer>(this.dfsTreeTemp));
this.backEdges.add(new HashMap<Integer, ArrayList<Integer>>(
this.backEdgesTemp));
this.dfsTreeTemp.clear();
this.backEdgesTemp.clear();
}
}
}
/**
* Single step in dfs-spanning tree algorithm.
*
* @param u
* current node id.
* @param state
* state of all nodes.
*/
private void runDFS(final int u, final VertexState[] state) {
final VersantNode node = VersantGraph.this.getNodeInternal(u);
state[u - 1] = VertexState.progress;
for (final VersantEdge neighbourEdge : node.getEdges()) {
final VersantNode neighbourNode = neighbourEdge.getTarget();
final int i = neighbourNode.getBasicId();
if (state[i - 1] == VertexState.notVisited) {
this.runDFS(i, state);
} else {
ArrayList<Integer> backEdgesNodeList = this.backEdgesTemp.get(i);
if (backEdgesNodeList == null) {
backEdgesNodeList = new ArrayList<Integer>();
}
backEdgesNodeList.add(u);
this.backEdgesTemp.put(i, backEdgesNodeList);
}
}
state[u - 1] = VertexState.visited;
this.dfsTreeTemp.add(u);
}
}
/**
* Computes all shortest paths among all nodes in network.
*
* Results in predecessor matrix(?).
*/
public void floydWarshall() {
zooActivateRead();
/**
* The weight matrix.
*/
float[][] d;
/**
* The predecessor matrix.
*/
short[][] p;
/**
* The path count matrix.
*/
short[][] c;
final int nodesCount = VersantGraph.this.nodes.size();
d = new float[nodesCount][nodesCount];
p = new short[nodesCount][nodesCount];
c = new short[nodesCount][nodesCount];
this.initializeWeight(d, p, c);
FloydWarshall.fw(nodesCount, d, p, c);
FloydWarshall.storeMatrices(p, c, d, nodes.asMap());
}
/**
* Initializes weights in transitive closure matrix.
*
* @param d
* empty distances matrix.
* @param p
* empty predecessor matrix.
*/
private void initializeWeight(final float[][] d, final short[][] p,
final short[][] c) {
final int nodesCount = VersantGraph.this.nodes.size();
for (int i = 0; i < nodesCount; i++) {
Arrays.fill(d[i], Float.MAX_VALUE);
Arrays.fill(c[i], (short) 0);
}
for (final VersantEdge currentEdge : VersantGraph.this.getEdges()) {
int sID = currentEdge.getSource().getBasicId() - 1;
int tID = currentEdge.getTarget().getBasicId() - 1;
d[sID][tID] = currentEdge.getBasicValue();
p[sID][tID] = VersantGraph.NEIGHBOUR_NODE;
c[sID][tID] = 1;
}
}
// public HashMap<Object, RowIndex> getRowIndices() {
// // TODO Auto-generated method stub
// return null;
// }
//
// public void insertRowIndex(final RowIndex ri) {
// // TODO Auto-generated method stub
//
// }
/**
* Returns a shortest path between 2 nodes using this graph's predecessor
* matrix.
*
* @param source
* source node.
* @param target
* target node.
* @return List of nodes in the path.
*/
public ArrayList<Integer> shortestPath(final int source, final int target) {
zooActivateRead();
ArrayList<Integer> path = new ArrayList<Integer>();
final int predecessor = this.nodes.get(source).getRowIndex().get(target)
.getPredecessor();
if (predecessor == VersantGraph.UNREACHABLE_NODE) {
return path;
}
if (predecessor != VersantGraph.NEIGHBOUR_NODE) {
path = this.getIntermediatePathRowIndex(source, target);
}
path.add(0, source);
path.add(target);
return path;
}
/**
* Returns incomplete shortest path between 2 nodes using this graph's
* predecessor matrix.
*
* @param source
* source node.
* @param target
* target node.
* @return list of nodes in the path.
*/
private ArrayList<Integer> getIntermediatePathRowIndex(final Integer source,
final Integer target) {
final Map<Integer, EdgePropertiesImpl> ri = VersantGraph.this.getRowIndex(source);
final ArrayList<Integer> path = new ArrayList<Integer>();
//TODO
final Integer predecessor = ri.get(target).getPredecessor();
if (ri.get(target).getPredecessor() > 0) {
path.addAll(this.getIntermediatePathRowIndex(source, predecessor));
path.add(predecessor);
path.addAll(this.getIntermediatePathRowIndex(predecessor, target));
}
return path;
}
}