/// <summary> /// Add the given list of paths to local variable. /// </summary> /// <param name="pathsList">List of paths to add.</param> public void AddPath(DeBruijnPath path) { this.paths.Add(path); }
/// <summary> /// Try and extend dangling links following /// graph clean-up after erosion. /// </summary> /// <param name="isForwardDirection">Boolean indicating direction of dangling link.</param> /// <param name="danglingLink">Dangling Link.</param> /// <param name="node">Node that is next on the link.</param> /// <param name="sameOrientation">Orientation of link.</param> /// <param name="removeLast">Boolean indicating if last node /// in link has to be removed before extending.</param> /// <returns>Length of dangling link found after extension.</returns> private int ExtendDanglingLink(bool isForwardDirection, DeBruijnPath danglingLink, DeBruijnNode node, bool sameOrientation, bool removeLast) { if (removeLast) { danglingLink.PathNodes.Remove(node); } if (danglingLink.PathNodes.Count == 0) { // DanglingLink is empty. So check if node is an end-point. if (node.RightExtensionNodesCount == 0) { danglingLink = TraceDanglingExtensionLink(false, new DeBruijnPath(), node, true); } else if (node.LeftExtensionNodesCount == 0) { danglingLink = TraceDanglingExtensionLink(true, new DeBruijnPath(), node, true); } else { // Not an end-point. Return length as 0 return 0; } } else { // Extend existing link. danglingLink = TraceDanglingExtensionLink(isForwardDirection, danglingLink, node, sameOrientation); } // Return length of dangling link found. return danglingLink == null ? 0 : danglingLink.PathNodes.Count; }
/// <summary> /// Starting from potential end of dangling link, trace back along /// extension edges in graph to find if it is a valid dangling link. /// Parallelization Note: No locks used in TraceDanglingLink. /// We only read graph structure here. No modifications are made. /// </summary> /// <param name="isForwardDirection">Boolean indicating direction of dangling link.</param> /// <param name="link">Dangling Link.</param> /// <param name="node">Node that is next on the link.</param> /// <param name="sameOrientation">Orientation of link.</param> /// <returns>List of nodes in dangling link.</returns> private DeBruijnPath TraceDanglingExtensionLink(bool isForwardDirection, DeBruijnPath link, DeBruijnNode node, bool sameOrientation) { for (; ;) { // Get extensions going in same and opposite directions. Dictionary<DeBruijnNode, bool> sameDirectionExtensions; int sameDirectionExtensionsCount, oppDirectionExtensionsCount; if (isForwardDirection ^ sameOrientation) { sameDirectionExtensionsCount = node.LeftExtensionNodesCount; oppDirectionExtensionsCount = node.RightExtensionNodesCount; sameDirectionExtensions = node.GetLeftExtensionNodesWithOrientation(); } else { sameDirectionExtensionsCount = node.RightExtensionNodesCount; oppDirectionExtensionsCount = node.LeftExtensionNodesCount; sameDirectionExtensions = node.GetRightExtensionNodesWithOrientation(); } bool reachedEndPoint; if (sameDirectionExtensionsCount == 0) { // Found other end of dangling link return CheckAndAddDanglingNode(link, node, out reachedEndPoint); } if (oppDirectionExtensionsCount > 1) { // Have reached a point of ambiguity. Return list without updating it. if (this.erodeThreshold != -1 && !node.IsMarkedForDelete) { lock (this.danglingLinkExtensionTasks) { // This task essentially just returns back to this method after other ones are removed this.danglingLinkExtensionTasks.Add(new Task<int>(_ => ExtendDanglingLink(isForwardDirection, link, node, sameOrientation, false), TaskCreationOptions.None)); } return null; } return link; } if (sameDirectionExtensionsCount > 1) { // Have reached a point of ambiguity. Return list after updating it. link = CheckAndAddDanglingNode(link, node, out reachedEndPoint); if (this.erodeThreshold != -1 && reachedEndPoint != true && !node.IsMarkedForDelete) { lock (this.danglingLinkExtensionTasks) { this.danglingLinkExtensionTasks.Add(new Task<int>(_ => ExtendDanglingLink(isForwardDirection, link, node, sameOrientation, true), TaskCreationOptions.None)); } return null; } return link; } // (sameDirectionExtensions == 1 && oppDirectionExtensions == 1) // Continue trace back. Add this node to that list and recurse. link = CheckAndAddDanglingNode(link, node, out reachedEndPoint); if (reachedEndPoint) { // Loop is found or threshold length has been exceeded. return link; } // Still in loop, so just add the extension and keeps going var item = sameDirectionExtensions.First(); node = item.Key; sameOrientation = !(sameOrientation ^ item.Value); } }
/// <summary> /// Checks if 'node' can be added to 'link' without /// violating any conditions pertaining to dangling links. /// Returns null if loop is found or length exceeds threshold. /// Otherwise, adds node to link and returns. /// </summary> /// <param name="link">Dangling link.</param> /// <param name="node">Node to be added.</param> /// <param name="reachedErrorEndPoint">Indicates if we have reached end of dangling link.</param> /// <returns>Updated dangling link.</returns> private DeBruijnPath CheckAndAddDanglingNode(DeBruijnPath link, DeBruijnNode node, out bool reachedErrorEndPoint) { if (this.erodeThreshold != -1 && link.PathNodes.Count == 0 && node.KmerCount < this.erodeThreshold) { if (node.IsMarkedForDelete) { // There is a loop in this link. No need to update link. // Set flag for end point reached as true and return. reachedErrorEndPoint = true; return link; } node.MarkNodeForDelete(); reachedErrorEndPoint = false; return link; } if (link.PathNodes.Contains(node)) { // There is a loop in this link. No need to update link. // Set flag for end point reached as true and return. reachedErrorEndPoint = true; return link; } if (link.PathNodes.Count >= LengthThreshold) { // Length crosses threshold. Not a dangling link. // So set reached error end point as true and return null. reachedErrorEndPoint = true; return null; } // No error conditions found. Add node to link. reachedErrorEndPoint = false; link.PathNodes.Add(node); return link; }
/// <summary> /// Removes nodes in link from the graph. /// Parallelization Note: Locks required here. We are modifying graph structure here. /// </summary> /// <param name="nodes">List of nodes to remove.</param> /// <param name="lastNodes">Set of all nodes occurring at end of dangling links.</param> private static void RemoveLinkNodes(DeBruijnPath nodes, HashSet<DeBruijnNode> lastNodes) { // Nodes in the list are part of a single dangling link. // Only the last element of link can have left or right extensions that are valid parts of graph. DeBruijnNode linkStartNode = nodes.PathNodes.Last(); // Update adjacency of nodes connected to the last node. // Read lock not required as linkStartNode's dictionary will not get updated // Locks used during removal of extensions. foreach (DeBruijnNode graphNode in linkStartNode.GetExtensionNodes()) { // Condition to avoid updating other linkStartNode's dictionary. Reduces conflicts. if (!lastNodes.Contains(graphNode)) { graphNode.RemoveExtensionThreadSafe(linkStartNode); } } }