001package org.biopax.paxtools.query.algorithm; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import org.biopax.paxtools.query.model.Edge; 006import org.biopax.paxtools.query.model.GraphObject; 007import org.biopax.paxtools.query.model.Node; 008 009import java.util.Collection; 010import java.util.HashMap; 011import java.util.LinkedList; 012import java.util.Map; 013import java.util.Set; 014 015/** 016 * Implements breadth-first search. Takes a set of source nodes, distance limit and labels nodes 017 * towards one direction, with their breadth distances. 018 * 019 * @author Ozgun Babur 020 * @author Merve Cakir 021 */ 022public class BFS 023{ 024 private static Log LOG = LogFactory.getLog(BFS.class); 025 026 /** 027 * Distance labels. Missing label interpreted as infinitive. 028 */ 029 protected Map<GraphObject, Integer> dist; 030 031 /** 032 * Color labels. Missing color interpreted as white. 033 */ 034 protected Map<GraphObject, Integer> colors; 035 036 /** 037 * BFS starts from source nodes. They get the label 0. 038 */ 039 protected Set<Node> sourceSet; 040 041 /** 042 * BFS will not further traverse neighbors of any node in the stopSet. 043 */ 044 protected Set<Node> stopSet; 045 046 /** 047 * Whether the direction is FORWARD, it is REVERSE otherwise. 048 */ 049 protected Direction direction; 050 051 /** 052 * Stop distance. 053 */ 054 protected int limit; 055 056 /** 057 * BFS queue. 058 */ 059 protected LinkedList<Node> queue; 060 061 /** 062 * Constructor with all parameters. 063 * @param sourceSet Seed of BFS 064 * @param stopSet Nodes that won't be traversed 065 * @param direction Direction of the traversal 066 * @param limit Distance limit 067 */ 068 public BFS(Set<Node> sourceSet, Set<Node> stopSet, Direction direction, int limit) 069 { 070 if (direction != Direction.UPSTREAM && direction != Direction.DOWNSTREAM) 071 throw new IllegalArgumentException("Direction has to be either upstream or downstream"); 072 073 this.sourceSet = sourceSet; 074 this.stopSet = stopSet; 075 this.direction = direction; 076 this.limit = limit; 077 } 078 079 /** 080 * Empty constructor for other possible uses. 081 */ 082 public BFS() 083 { 084 } 085 086 /** 087 * Executes the algorithm. 088 * @return BFS tree 089 */ 090 public Map<GraphObject, Integer> run() 091 { 092 initMaps(); 093 094 // Add all source nodes to the queue if traversal is needed 095 096 if (limit > 0) 097 { 098 queue.addAll(sourceSet); 099 } 100 101 // Initialize dist and color of source set 102 103 for (Node source : sourceSet) 104 { 105 setLabel(source, 0); 106 setColor(source, GRAY); 107 108 labelEquivRecursive(source, UPWARD, 0, true, false); 109 labelEquivRecursive(source, DOWNWARD, 0, true, false); 110 } 111 112 // Process the queue 113 114 while (!queue.isEmpty()) 115 { 116 Node current = queue.remove(0); 117 118 processNode(current); 119 120 // Current node is processed 121 setColor(current, BLACK); 122 } 123 124 return dist; 125 } 126 127 /** 128 * Initializes maps used during query. 129 */ 130 protected void initMaps() 131 { 132 // Initialize label, maps and queue 133 134 dist = new HashMap<GraphObject, Integer>(); 135 colors = new HashMap<GraphObject, Integer>(); 136 queue = new LinkedList<Node>(); 137 } 138 139 /** 140 * Processes a node. 141 * @param current The current node 142 */ 143 protected void processNode(Node current) 144 { 145 // Do not process the node if it is ubique 146 147 if (current.isUbique()) 148 { 149 setColor(current, BLACK); 150 return; 151 } 152 153// System.out.println("processing = " + current); 154 155 // Process edges towards the direction 156 157 for (Edge edge : direction == Direction.DOWNSTREAM ? 158 current.getDownstream() : current.getUpstream()) 159 { 160 assert edge != null; 161 162 // Label the edge considering direction of traversal and type of current node 163 164 if (direction == Direction.DOWNSTREAM || !current.isBreadthNode()) 165 { 166 setLabel(edge, getLabel(current)); 167 } 168 else 169 { 170 setLabel(edge, getLabel(current) + 1); 171 } 172 173 // Get the other end of the edge 174 Node neigh = direction == Direction.DOWNSTREAM ? 175 edge.getTargetNode() : edge.getSourceNode(); 176 177 assert neigh != null; 178 179 // Decide neighbor label according to the search direction and node type 180 int dist = getLabel(edge); 181 if (neigh.isBreadthNode() && direction == Direction.DOWNSTREAM) dist++; 182 183 // Check if we need to stop traversing the neighbor, enqueue otherwise 184 boolean further = (stopSet == null || !isEquivalentInTheSet(neigh, stopSet)) && 185 (!neigh.isBreadthNode() || dist < limit) && !neigh.isUbique(); 186 187 // Process the neighbor if not processed or not in queue 188 189 if (getColor(neigh) == WHITE) 190 { 191 // Label the neighbor 192 setLabel(neigh, dist); 193 194 if (further) 195 { 196 setColor(neigh, GRAY); 197 198 // Enqueue the node according to its type 199 200 if (neigh.isBreadthNode()) 201 { 202 queue.addLast(neigh); 203 } 204 else 205 { 206 // Non-breadth nodes are added in front of the queue 207 queue.addFirst(neigh); 208 } 209 } 210 else 211 { 212 // If we do not want to traverse this neighbor, we paint it black 213 setColor(neigh, BLACK); 214 } 215 } 216 217 218 labelEquivRecursive(neigh, UPWARD, getLabel(neigh), further, !neigh.isBreadthNode()); 219 labelEquivRecursive(neigh, DOWNWARD, getLabel(neigh), further, !neigh.isBreadthNode()); 220 } 221 } 222 223 /** 224 * Labels equivalent nodes recursively. 225 * @param node Node to label equivalents 226 * @param up Traversing direction. Up means towards parents, if false then towards children 227 * @param dist The label 228 * @param enqueue Whether to enqueue equivalents 229 * @param head Where to enqueue. Head or tail. 230 */ 231 protected void labelEquivRecursive(Node node, boolean up, int dist, 232 boolean enqueue, boolean head) 233 { 234 if(node == null) { 235 LOG.error("labelEquivRecursive: null (Node)"); 236 return; 237 } 238 239 for (Node equiv : up ? node.getUpperEquivalent() : node.getLowerEquivalent()) 240 { 241 if (getColor(equiv) == WHITE) 242 { 243 setLabel(equiv, dist); 244 245 if (enqueue) 246 { 247 setColor(equiv, GRAY); 248 249 if (head) queue.addFirst(equiv); 250 else queue.add(equiv); 251 } 252 else 253 { 254 setColor(equiv, BLACK); 255 } 256 } 257 258 labelEquivRecursive(equiv, up, dist, enqueue, head); 259 } 260 } 261 262 /** 263 * Checks if an equivalent of the given node is in the set. 264 * @param node Node to check equivalents 265 * @param set Node set 266 * @return true if an equivalent is in the set 267 */ 268 protected boolean isEquivalentInTheSet(Node node, Set<Node> set) 269 { 270 return set.contains(node) || 271 isEquivalentInTheSet(node, UPWARD, set) || isEquivalentInTheSet(node, DOWNWARD, set); 272 } 273 274 /** 275 * Checks if an equivalent of the given node is in the set. 276 * @param node Node to check equivalents 277 * @param direction Direction to go to get equivalents 278 * @param set Node set 279 * @return true if an equivalent is in the set 280 */ 281 protected boolean isEquivalentInTheSet(Node node, boolean direction, Set<Node> set) 282 { 283 for (Node eq : direction == UPWARD ? node.getUpperEquivalent() : node.getLowerEquivalent()) 284 { 285 if (set.contains(eq)) return true; 286 boolean isIn = isEquivalentInTheSet(eq, direction, set); 287 if (isIn) return true; 288 } 289 return false; 290 } 291 292 /** 293 * Gets color tag of the node 294 * @param node Node to get color tag 295 * @return color tag 296 */ 297 protected int getColor(Node node) 298 { 299 if (!colors.containsKey(node)) 300 { 301 // Absence of color is interpreted as white 302 return WHITE; 303 } 304 else 305 { 306 return colors.get(node); 307 } 308 } 309 310 /** 311 * Sets color tag 312 * @param node node to set color tag 313 * @param color the color tag 314 */ 315 protected void setColor(Node node, int color) 316 { 317 colors.put(node, color); 318 } 319 320 /** 321 * Gets the distance label of the object. 322 * @param go object to get the distance 323 * @return the distance label 324 */ 325 public int getLabel(GraphObject go) 326 { 327 if (!dist.containsKey(go)) 328 { 329 // Absence of label is interpreted as infinite 330 return Integer.MAX_VALUE-(limit*2); 331 } 332 else 333 { 334 return dist.get(go); 335 } 336 } 337 338 /** 339 * Sets the distance label. 340 * @param go object to set the distance label 341 * @param label the distance label 342 */ 343 protected void setLabel(GraphObject go, int label) 344 { 345// System.out.println("Labeling(" + label + "): " + go); 346 dist.put(go, label); 347 } 348 349 /** 350 * Forward traversal direction. 351 */ 352 public static final boolean FORWARD = true; 353 354 /** 355 * Backward traversal direction. 356 */ 357 public static final boolean BACKWARD = false; 358 359 /** 360 * Forward traversal direction. 361 */ 362 public static final boolean UPWARD = true; 363 364 /** 365 * Backward traversal direction. 366 */ 367 public static final boolean DOWNWARD = false; 368 369 /** 370 * Color white indicates the node is not processed. 371 */ 372 public static final int WHITE = 0; 373 374 /** 375 * Color gray indicates that the node is in queue waiting to be procecessed. 376 */ 377 public static final int GRAY = 1; 378 379 /** 380 * Color black indicates that the node was processed. 381 */ 382 public static final int BLACK = 2; 383}