private ExploreChildNodesResult ExploreChildNodes <TVertex>( QuiverWithPotential <TVertex> qp, TransformationRuleTreeNode <TVertex> transformationRuleTree, AnalysisStateForSingleStartingVertexOld <TVertex> state, SearchTreeNodeOld <TVertex> parentNode, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { bool pathHasNonzeroExtension = false; // Path has no nonzero extension until proven otherwise // Explore every child node (determine its equivalence class) foreach (var nextVertex in qp.Quiver.AdjacencyLists[parentNode.Vertex]) { var result = ExploreChildNode(transformationRuleTree, state, parentNode, nextVertex, settings); switch (result) { case ExploreChildNodeResult.NotZeroEquivalent: pathHasNonzeroExtension = true; break; case ExploreChildNodeResult.NonCancellativityDetected: return(ExploreChildNodesResult.NonCancellativityDetected); case ExploreChildNodeResult.TooLongPath: return(ExploreChildNodesResult.TooLongPath); } } return(pathHasNonzeroExtension ? ExploreChildNodesResult.PathHasNonzeroExtension : ExploreChildNodesResult.PathHasNoNonzeroExtension); }
// After figuring out the use cases (high performance?) for these methods and figuring out // whether to change the interface, implement the counterparts of the methods in // SemimonomialUnboundQuiverAnalyzer and have the public methods of this class just be a // wrapper for said methods in SemimonomialUnboundQuiverAnalyzer (like the Analyze method // is right now). #region Code to refactor public AnalysisResultsForSingleStartingVertexOld <TVertex> AnalyzeWithStartingVertex <TVertex>( QuiverWithPotential <TVertex> qp, TVertex startingVertex, TransformationRuleTreeNode <TVertex> transformationRuleTree, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Parameter validation if (qp == null) { throw new ArgumentNullException(nameof(qp)); } if (!qp.Quiver.Vertices.Contains(startingVertex)) { throw new ArgumentException($"The QP does not contain the starting vertex {startingVertex}."); } // Set up for analysis/graph search var state = new AnalysisStateForSingleStartingVertexOld <TVertex>(startingVertex); bool cancellativityFailed = false; bool tooLongPathEncountered = false; // Analysis/graph search // Keep a stack of "recently explored" nodes // In every iteration, pop a recently explored node from the stack and explore (determine // the equivalence classes of) its child nodes. // It would be cleaner to in every iteration explore the current node (determine its equivalence class) // and discover its child nodes (push new equivalence class representatives (which may overlap?) onto // the stack for future iterations, but this makes it non-trivial to keep track of the maximal nonzero // equivalence classes while (state.Stack.Count > 0) { var node = state.Stack.Pop(); var result = ExploreChildNodes(qp, transformationRuleTree, state, node, settings); if (result == ExploreChildNodesResult.PathHasNoNonzeroExtension) { state.maximalPathRepresentatives.Add(node); } else if (result == ExploreChildNodesResult.NonCancellativityDetected) { cancellativityFailed = true; break; } else if (result == ExploreChildNodesResult.TooLongPath) { tooLongPathEncountered = true; break; } } // Return results var results = new AnalysisResultsForSingleStartingVertexOld <TVertex>(state, cancellativityFailed, tooLongPathEncountered); return(results); }
internal AnalysisResultsForSingleStartingVertexOld( AnalysisStateForSingleStartingVertexOld <TVertex> state, bool nonCancellativityDetected, bool tooLongPathEncountered) { ZeroDummyNode = state.ZeroDummyNode; SearchTree = state.SearchTree; MaximalPathRepresentatives = state.MaximalPathRepresentatives; EquivalenceClasses = state.EquivalenceClasses; NonCancellativityDetected = nonCancellativityDetected; TooLongPathEncountered = tooLongPathEncountered; }
/// <returns>A boolean value indicating whether the node was found to be zero-equivalent. /// That is, the returned value is <see langword="true"/> <em>only</em> if the node is /// zero-equivalent but may be <see langword="false"/> even if the node is zero-equivalent.</returns> /// <remarks>A node is found to be zero-equivalent either if its path can be killed or if /// the path is equivalent to (up to replacement) to a path that has previously been /// determined to be zero-equivalent.</remarks> private static bool EquivClassExploreNode <TVertex>( TransformationRuleTreeNode <TVertex> transformationRuleTree, SearchTreeNodeOld <TVertex> node, AnalysisStateForSingleStartingVertexOld <TVertex> state, Stack <SearchTreeNodeOld <TVertex> > equivClassStack, SearchTreeNodeOld <TVertex> origin) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { node.Explored = true; var trailingVertexPath = new List <TVertex>(); // In reversed order foreach (var endingNode in node.ReversePathOfNodes) // The last vertex of endingNode is the last vertex in the subpath { var transformationNode = transformationRuleTree; foreach (var startingNode in endingNode.ReversePathOfNodes) // The last vertex of startingNode is the first vertex in the subpath { var pathVertex = startingNode.Vertex; if (!transformationNode.Children.TryGetValue(pathVertex, out transformationNode)) { break; } if (transformationNode.CanBeKilled) { state.EquivalenceClasses.Union(node, state.ZeroDummyNode); return(true); } // If replacement is possible, do the replacement on the subpath // and add a search tree node for the entire resulting path if (transformationNode.CanBeReplaced) { // Add search tree nodes for replacement path // This doesn't work when pathNode is the root ... // var lastUnreplacedNode = pathNode.Parent; // var curNode = lastUnreplacedNode; // ... so instead do the following (with Skip), which assumes that the replacement // path has the same first vertex (which it does for the QPs under consideration) var firstReplacementNode = startingNode; var curNode = firstReplacementNode; foreach (var vertex in transformationNode.ReplacementPath.Vertices.Skip(1)) { curNode = state.GetInsertChildNode(curNode, vertex, origin); } // Add search tree nodes for the trailing path foreach (var vertex in trailingVertexPath.Reversed()) { curNode = state.GetInsertChildNode(curNode, vertex, origin); } var transformedNode = curNode; if (state.NodeIsZeroEquivalent(transformedNode)) { state.EquivalenceClasses.Union(node, transformedNode); return(true); } // transformedNode.Explored may be true and is then a node that we have // already encountered during the equivalence class search. // (It cannot be an explored node of some *other* equivalence class, // because then this current equivalence class search would not have // started in the first place.) //if (!transformedNode.Explored) if (!state.EquivalenceClasses.FindSet(node).Equals(state.EquivalenceClasses.FindSet(transformedNode))) { state.EquivalenceClasses.Union(node, transformedNode); equivClassStack.Push(transformedNode); } } } trailingVertexPath.Add(endingNode.Vertex); } return(false); }
/// <returns>A boolean value indicating whether the path correspondings to the explored /// child node is nonzero (up to equivalence).</returns> private ExploreChildNodeResult ExploreChildNode <TVertex>( TransformationRuleTreeNode <TVertex> transformationRuleTree, AnalysisStateForSingleStartingVertexOld <TVertex> state, SearchTreeNodeOld <TVertex> parentNode, TVertex nextVertex, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Either the child node has not already been discovered, or the child node was // discovered during an equivalence class search. if (parentNode.Children.TryGetValue(nextVertex, out var childNode)) { // The child node has already been discovered. // Do a non-cancellativity check (if we are to detect non-cancellativity): // If the child node is zero, everything is fine. // If a different arrow was added to the origin than to the parent to get child node (i.e., origin and parent have different last vertices), things are fine. // Else, make sure that the origin is equivalent to the parent // (otherwise, the QP is not cancellative). if ( settings.DetectCancellativityFailure && !state.NodeIsZeroEquivalent(childNode) && parentNode.Vertex.Equals(childNode.Origin.Vertex) && state.EquivalenceClasses.FindSet(parentNode) != state.EquivalenceClasses.FindSet(childNode.Origin)) { return(ExploreChildNodeResult.NonCancellativityDetected); } } else { // The child node has not been discovered, so let's discover it by inserting it // into the search tree. childNode = state.InsertChildNode(parentNode, nextVertex, parentNode); } if (childNode.Explored) { return(state.NodeIsZeroEquivalent(childNode) ? ExploreChildNodeResult.ZeroEquivalent : ExploreChildNodeResult.NotZeroEquivalent); } // Code for equivalence class searching begins here // Stack containing all the nodes to explore (by path transformation) var equivClassStack = new Stack <SearchTreeNodeOld <TVertex> >(state.EquivalenceClasses.GetSet(childNode)); // This could happen with the current code, namely when the current childNode was encountered // in a previous call to ExploreChildNodes (in EquivClassExploreNode, as transformationNode) // and 'node' was found to be zero equivalent (in which case the search is cancelled // before transformationNode is explored in EquivClassExploreNode) // Thought: Would probably be good to have several Explored properties (or an // enum-valued Explored property) containing information about the extent to which the // node is explored (explored-as-in-encountered-in-EquivClassExploreNode would be // useful here; more easily understood and maintained than this comment methinks) if (equivClassStack.Count != 1) { return(ExploreChildNodeResult.ZeroEquivalent); } while (equivClassStack.Count > 0) { var equivalentNode = equivClassStack.Pop(); if (equivalentNode.Explored) { continue; } if (settings.UseMaxLength && equivalentNode.PathLengthInVertices > settings.MaxPathLength + 1) { return(ExploreChildNodeResult.TooLongPath); } var exploredNodeWasFoundZeroEquivalent = EquivClassExploreNode(transformationRuleTree, equivalentNode, state, equivClassStack, parentNode); if (exploredNodeWasFoundZeroEquivalent) { return(ExploreChildNodeResult.ZeroEquivalent); } } // Child node was not zero-equivalent state.Stack.Push(childNode); return(ExploreChildNodeResult.NotZeroEquivalent); }