/// <summary> /// /// </summary> /// <typeparam name="TVertex">The type of the vertices in the quiver.</typeparam> /// <param name="quiver">The quiver.</param> /// <param name="startingVertex">The starting vertex of the paths to look at.</param> /// <param name="transformationRuleTree"></param> /// <returns>The state of the search after it is done.</returns> private AnalysisStateForSingleStartingVertex <TVertex> DoSearchInPathTree <TVertex>( Quiver <TVertex> quiver, TVertex startingVertex, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Set up for analysis/graph search var state = new AnalysisStateForSingleStartingVertex <TVertex>(startingVertex); // Analysis/graph search // Keep a stack of "recently equivalence-class-computed" nodes // In every iteration, pop a node from the stack and determine the equivalence classes // of its child nodes. // It would be cleaner to in every iteration determine the equivalence class of the // *current* node and enqueue its child nodes (in other words, to have the queue // contain nodes whose equivalence classes to explore), 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(); bool isMaximalNonzero = true; foreach (var nextVertex in quiver.AdjacencyLists[node.Vertex]) { var child = state.GetInsertChildNode(node, nextVertex); if (DoPathLengthCheck(child, state, settings) == PathLengthCheckResult.TooLongPath) { state.TooLongPathEncountered = true; return(state); } var equivClassResult = DetermineEquivalenceClass(child, state, transformationRuleTree, settings); if (equivClassResult == EquivalenceClassResult.Nonzero) { isMaximalNonzero = false; state.Stack.Push(child); } else if (equivClassResult == EquivalenceClassResult.TooLongPath) { state.TooLongPathEncountered = true; return(state); } } if (isMaximalNonzero) { var representative = state.EquivalenceClasses.FindSet(node); if (!state.maximalPathRepresentatives.Contains(representative)) { state.maximalPathRepresentatives.Add(representative); } } } return(state); }
/// <summary> /// Explores the specified node in the equivalence class search. /// </summary> /// <typeparam name="TVertex">The type of the vertices in the quiver.</typeparam> /// <param name="node">The node to explore.</param> /// <param name="equivClassStack">The stack or queue of nodes to explore.</param> /// <param name="state">The state of the computation.</param> /// <param name="transformationRuleTree">The transformation rule tree.</param> /// <param name="settings">The computation settings.</param> /// <returns>A value of the <see cref="EquivalenceClassResult"/> enum indicating whether /// the node was <em>found</em> to be zero-equivalent. That is, the returned value is /// <see cref="EquivalenceClassResult.Zero"/> <em>only</em> if the node is zero-equivalent /// but may be <see cref="EquivalenceClassResult.Nonzero"/> even if the node is /// zero-equivalent. /// <remarks> /// <para>A node is found to be zero-equivalent either if its path can be killed or if /// the path is equivalent (up to replacement) to a path that has previously been /// determined to be zero-equivalent.</para> /// <para>This method may modify the /// <see cref="AnalysisStateForSingleStartingVertex{TVertex}.SearchTree"/>, /// <see cref="AnalysisStateForSingleStartingVertex{TVertex}.EquivalenceClasses"/>, and /// <see cref="AnalysisStateForSingleStartingVertex{TVertex}.LongestPathEncounteredNode"/> /// properties of the <paramref name="state"/> argument.</para> /// </remarks> private EquivalenceClassResult ExploreNodeForEquivalenceClassSearch <TVertex>( SearchTreeNode <TVertex> node, Stack <SearchTreeNode <TVertex> > equivClassStack, AnalysisStateForSingleStartingVertex <TVertex> state, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // A list of the vertices that appear after the path being considered from transformation. // The vertices are in reversed order. var trailingVertexPath = new List <TVertex>(); foreach (var endingNode in node.ReversePathOfNodes) // The vertex (i.e., last vertex) of endingNode is the last vertex in the subpath { var transformationNode = transformationRuleTree; foreach (var startingNode in endingNode.ReversePathOfNodes) // The vertex (i.e., 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(EquivalenceClassResult.Zero); } // 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 semimonomial unbound // quivers under consideration) var firstNodeInTransformedPath = startingNode; var curNode = firstNodeInTransformedPath; foreach (var vertex in transformationNode.ReplacementPath.Vertices.Skip(1)) { curNode = state.GetInsertChildNode(curNode, vertex); } // Add search tree nodes for the trailing path foreach (var vertex in trailingVertexPath.Reversed()) { curNode = state.GetInsertChildNode(curNode, vertex); } // We now have the node obtained by applying the replacement rule. var transformedNode = curNode; // Do stuff with its path length. bool tooLong = DoPathLengthCheck(transformedNode, state, settings) == PathLengthCheckResult.TooLongPath; if (tooLong) { return(EquivalenceClassResult.TooLongPath); } if (state.NodeIsZeroEquivalent(transformedNode)) { state.EquivalenceClasses.Union(node, transformedNode); // Unioning with state.ZeroDummyNode would work equally well return(EquivalenceClassResult.Zero); } // If transformedNode.HasBeenDiscoveredDuringEquivalenceClassComputation // at this point, then it was discovered during this equivalence class // search (not during an earlier search that ended with zero equivalence), // and in this case, we should ignore it! if (!transformedNode.HasBeenDiscoveredDuringEquivalenceClassComputation) { transformedNode.HasBeenDiscoveredDuringEquivalenceClassComputation = true; state.EquivalenceClasses.Union(node, transformedNode); equivClassStack.Push(transformedNode); } } } trailingVertexPath.Add(endingNode.Vertex); } return(EquivalenceClassResult.Nonzero); }