001package org.biopax.paxtools.query.algorithm; 002 003import org.biopax.paxtools.query.model.Edge; 004import org.biopax.paxtools.query.model.GraphObject; 005import org.biopax.paxtools.query.model.Node; 006 007import java.util.ArrayList; 008import java.util.Collection; 009import java.util.Set; 010 011/** 012 * When an algorithm searches for a paths between multiple source nodes, or from a source to a 013 * target node (source and targets may overlap), sometimes there comes paths from and to the same 014 * node, i.e cycles. When we want to avoid these cases this class can be used to remove those 015 * cycles in the result set. 016 * 017 * @author Ozgun Babur 018 */ 019public class CycleBreaker extends BFS 020{ 021 /** 022 * The result set to search for cycles. 023 */ 024 Set<GraphObject> result; 025 026 /** 027 * Source and (if exists) target nodes. 028 */ 029 Set<Node> ST; 030 031 /** 032 * Constructor with the objects in the result, source and target nodes, and search limit. 033 * @param result Result set to search in 034 * @param ST Source and target nodes 035 * @param limit Search limit 036 */ 037 public CycleBreaker(Set<GraphObject> result, Set<Node> ST, int limit) 038 { 039 this.result = result; 040 this.ST = ST; 041 this.limit = limit; 042 } 043 044 /** 045 * Run the algorithm. 046 */ 047 public void breakCycles() 048 { 049 for (GraphObject go : new ArrayList<GraphObject>(result)) 050 { 051 if (go instanceof Node) 052 { 053 Node node = (Node) go; 054 055 for (Edge edge : node.getDownstream()) 056 { 057 if (result.contains(edge) && !isSafe(node, edge)) 058 { 059 result.remove(edge); 060 } 061 } 062 } 063 } 064 } 065 066 /** 067 * Checks whether an edge is on an unwanted cycle. 068 * @param node Node that the edge is bound 069 * @param edge The edge to check 070 * @return True if no cycle is detected, false otherwise 071 */ 072 public boolean isSafe(Node node, Edge edge) 073 { 074 initMaps(); 075 076 setColor(node, BLACK); 077 setLabel(node, 0); 078 setLabel(edge, 0); 079 080 labelEquivRecursive(node, UPWARD, 0, false, false); 081 labelEquivRecursive(node, DOWNWARD, 0, false, false); 082 083 // Initialize dist and color of source set 084 085 Node neigh = edge.getTargetNode(); 086 087 if (getColor(neigh) != WHITE) return false; 088 089 setColor(neigh, GRAY); 090 setLabel(neigh, 0); 091 092 queue.add(neigh); 093 094 labelEquivRecursive(neigh, UPWARD, 0, true, false); 095 labelEquivRecursive(neigh, DOWNWARD, 0, true, false); 096 097 // Process the queue 098 099 while (!queue.isEmpty()) 100 { 101 Node current = queue.remove(0); 102 103 if (ST.contains(current)) return true; 104 105 boolean safe = processNode2(current); 106 107 if (safe) return true; 108 109 // Current node is processed 110 setColor(current, BLACK); 111 } 112 113 return false; 114 } 115 116 /** 117 * Continue the search from the node. 2 is added because a name clash in the parent class. 118 * @param current The node to traverse 119 * @return False if a cycle is detected 120 */ 121 protected boolean processNode2(Node current) 122 { 123 return processEdges(current, current.getDownstream()) || 124 processEdges(current, current.getUpstream()); 125 } 126 127 /** 128 * Continue evaluating the next edge. 129 * @param current Current node 130 * @param edges The edge to evaluate 131 * @return False if a cycle is detected 132 */ 133 private boolean processEdges(Node current, Collection<Edge> edges) 134 { 135 for (Edge edge : edges) 136 { 137 if (!result.contains(edge)) continue; 138 139 // Label the edge considering direction of traversal and type of current node 140 141 setLabel(edge, getLabel(current)); 142 143 // Get the other end of the edge 144 Node neigh = edge.getSourceNode() == current ? 145 edge.getTargetNode() : edge.getSourceNode(); 146 147 // Process the neighbor if not processed or not in queue 148 149 if (getColor(neigh) == WHITE) 150 { 151 // Label the neighbor according to the search direction and node type 152 153 if (neigh.isBreadthNode()) 154 { 155 setLabel(neigh, getLabel(current) + 1); 156 } 157 else 158 { 159 setLabel(neigh, getLabel(edge)); 160 } 161 162 // Check if we need to stop traversing the neighbor, enqueue otherwise 163 164 if (getLabel(neigh) == limit || isEquivalentInTheSet(neigh, ST)) 165 { 166 return true; 167 } 168 169 setColor(neigh, GRAY); 170 171 // Enqueue the node according to its type 172 173 if (neigh.isBreadthNode()) 174 { 175 queue.addLast(neigh); 176 } 177 else 178 { 179 // Non-breadth nodes are added in front of the queue 180 queue.addFirst(neigh); 181 } 182 183 labelEquivRecursive(neigh, UPWARD, getLabel(neigh), true, !neigh.isBreadthNode()); 184 labelEquivRecursive(neigh, DOWNWARD, getLabel(neigh), true, !neigh.isBreadthNode()); 185 } 186 } 187 return false; 188 } 189}