package edu.isi.karma.modeling.steiner.topk;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
/**
* This class represents an (intermediate) result of a Steiner tree query.
* Such a result is a tree which may be improved to a better result when ever this is possible
* (see paper). It consists of treeNodes (all the nodes) and terminalNodes (query nodes).
* @author kasneci
*
*/
public class ApprSteinerTree extends SteinerSubTree implements Cloneable {
// all the nodes of the result tree
protected Set<SteinerNode> treeNodes;
// nodes representing the query terms
protected Set<SteinerNode> terminalNodes;
// needed for a dynamic programming implementation
protected SteinerNode root;
public ApprSteinerTree(Set<SteinerNode>terminalNodes, Set<SteinerNode>treeNodes){
super(treeNodes);
this.terminalNodes=terminalNodes;
this.treeNodes=treeNodes;
}
/**
* copying this ApprSteinerTree
* @return copy of this ApprSteinerTree
*/
public ApprSteinerTree clone(){
Set<SteinerNode> newTerminals= new TreeSet<>();
Set<SteinerNode> newTreeNodes= new TreeSet<>();
Map<String,SteinerNode> hms= new HashMap<>();
// copy nodes (only nodeIds: SteinerNode.copy())
for(SteinerNode n: treeNodes){
hms.put(n.getNodeId(), n.copy());
}
// copy edges of each node
for(SteinerNode n: treeNodes){
for(SteinerEdge e: n.edges){
if((hms.get(e.sourceNode.getNodeId())!=null)&&(hms.get(e.sinkNode.getNodeId())!=null)){
hms.get(e.sourceNode.getNodeId()).addEdge(
hms.get(e.sinkNode.getNodeId()), false, e.label().name, e.weight());
hms.get(e.sinkNode.getNodeId()).addEdge(
hms.get(e.sourceNode.getNodeId()), true, e.label().name, e.weight());
}
}
}
// add terminal nodes
for(SteinerNode n: hms.values()){
if(terminalNodes.contains(n)){
newTerminals.add(n);
}
newTreeNodes.add(n);
}
return new ApprSteinerTree(newTerminals,newTreeNodes);
}
/**
* retrieves all nodes that have degree >= 3 and the terminals (fixedNodes).
* @return TreeSet<SteinerNode> of all nodes that have degree >= 3 and the terminals
*/
public Set<SteinerNode> getFixedNodes(){
Set<SteinerNode> fixedNodes= new TreeSet<>();
// first add all terminals
fixedNodes.addAll(terminalNodes);
// than add all fixedNodes
for(SteinerNode node: treeNodes){
if(node.isFixedNode()){
fixedNodes.add(node);
}
}
return fixedNodes;
}
/**
*
* @param p loose path to be removed
* @return the subtrees resulting from the removal of p
*/
public List<Set<SteinerNode>> getNodeSetPartitioning(LoosePath p){
List<Set<SteinerNode>> listOfPartitions = new ArrayList<>();
SteinerNode n1 = p.getFirstNode();
SteinerNode n2 = p.getLastNode();
for(SteinerNode node:treeNodes){
if(node.equals(n1))
/*get all nodes that can be reached by following all edges connected
to n1's pendant in the tree (i.e. node) except for the first edge of n1.*/
listOfPartitions.add(getNodesInBreadthFirstFrom(node, n1.edges.first()));
if(node.equals(n2))
listOfPartitions.add(getNodesInBreadthFirstFrom(node, n2.edges.first()));
}
return listOfPartitions;
}
/**
*
* @param node the steiner node from which the breadth first search (BFS) should start.
* @param exceptForEdge the edge that should not be considered in the BFS
* @return the nodes reached by the BFS
*/
public Set<SteinerNode> getNodesInBreadthFirstFrom(SteinerNode node, SteinerEdge exceptForEdge){
Set<SteinerNode> ts = new TreeSet<>();
Queue<SteinerNode> queue= new LinkedList<>();
queue.offer(node);
while(!queue.isEmpty()){
SteinerNode n=queue.poll();
ts.add(n);
for(SteinerNode n1: n.getNeighbors(exceptForEdge)){
if(!ts.contains(n1)){
queue.offer(n1);
//ts.add(n1);
}
}
}
//removing bad edges...
for(SteinerNode neighbor:node.getNeighbors())
if(!ts.contains(neighbor))
node.deleteEdge(node.getEdgeToNode(neighbor));
return ts;
}
/**
* retireves all loose paths connected to fixed node
* @param fixedNode steiner node from which to start the search for loose paths
* @return PriorityQueue of all loose paths connected to fixedNode
*/
public PriorityQueue<LoosePath> getLoosePaths(SteinerNode fixedNode){
PriorityQueue<LoosePath> loosePaths= new PriorityQueue<>();
for(SteinerEdge edge: fixedNode.edges){
SteinerEdge e2=edge;
SteinerNode n1=new SteinerNode(fixedNode.getNodeId());
SteinerNode n= fixedNode.getNeighborInEdge(edge);
SteinerNode n2 = new SteinerNode(n.getNodeId());
if(edge.sourceNode.equals(n)){
n1.addEdge(n2, true, edge.getEdgeLabel(), edge.weight());
}
else{
n1.addEdge(n2, false, edge.getEdgeLabel(), edge.weight());
}
LinkedList<SteinerNode> pathNodes = new LinkedList<>();
pathNodes.add(n1);pathNodes.add(n2);
// following a loose path
while(!n.isFixedNode()&&!terminalNodes.contains(n)){
for(SteinerEdge e: n.edges)
if(!e2.equals(e)){
e2=e;
break;
}
n=n.getNeighborInEdge(e2);
SteinerNode n3= new SteinerNode(n.getNodeId());
if(e2.sourceNode.equals(n)){
n3.addEdge(n2, false, e2.getEdgeLabel(), e2.weight());
}
else{
n3.addEdge(n2, true, e2.getEdgeLabel(), e2.weight());
}
n2=n3;
pathNodes.add(n2);
}
loosePaths.add(new LoosePath(pathNodes));
}
return loosePaths;
}
/**
*
* @return priority queue of all loose paths in a tree (ranked by their scores)
*/
public Set<LoosePath> getLoosePaths(){
Set<LoosePath> loosePaths= new TreeSet<>();
for(SteinerNode node: treeNodes){
if(node.isFixedNode()||terminalNodes.contains(node)){
loosePaths.addAll(getLoosePaths(node));
}
}
return loosePaths;
}
public Set<SteinerNode> getTreeNodes() {
return treeNodes;
}
}