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); }
/// <summary> /// Computes the equivalence class of the specified node. /// </summary> /// <typeparam name="TVertex">The type of the vertices in the quiver.</typeparam> /// <param name="node">The node whose equivalence class to determine.</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><see cref="EquivalenceClassResult.TooLongPath"/> if /// <paramref name="settings"/> has <see cref="AnalysisSettings.UseMaxLength"/> equal to /// <see langword="true"/> and a path of length (in arrows) strictly greater than /// <see cref="AnalysisSettings.MaxPathLength"/> of <paramref name="settings"/> is /// encountered during the equivalence class search. Otherwise, /// <see cref="EquivalenceClassResult.Zero"/> if the equivalence class is the zero /// class, and <see cref="EquivalenceClassResult.Nonzero"/> if the equivalence class is not /// the zero class.</returns> /// <remarks> /// <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 ComputeEquivalenceClass <TVertex>( SearchTreeNode <TVertex> startNode, AnalysisStateForSingleStartingVertex <TVertex> state, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // The stack or queue of nodes to process var equivClassStack = new Stack <SearchTreeNode <TVertex> >(); equivClassStack.Push(startNode); startNode.HasBeenDiscoveredDuringEquivalenceClassComputation = true; while (equivClassStack.Count > 0) { var node = equivClassStack.Pop(); var result = ExploreNodeForEquivalenceClassSearch(node, equivClassStack, state, transformationRuleTree, settings); if (result == EquivalenceClassResult.TooLongPath) { return(EquivalenceClassResult.TooLongPath); // If too long, return "too long". } else if (result == EquivalenceClassResult.Zero) { return(EquivalenceClassResult.Zero); // If definitely zero-equivalent, return "zero" } } return(EquivalenceClassResult.Nonzero); }
/// <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); }
// 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); }
/// <summary> /// Essentially <see cref="ComputeMaximalNonzeroEquivalenceClassRepresentativesStartingAt"/> /// but with a more detailed, implementation-specific return value. /// </summary> /// <typeparam name="TVertex"></typeparam> /// <param name="quiver"></param> /// <param name="startingVertex"></param> /// <param name="transformationRuleTree"></param> /// <param name="settings"></param> /// <returns></returns> private AnalysisResultsForSingleStartingVertex <TVertex> AnalyzeWithStartingVertex <TVertex>( Quiver <TVertex> quiver, TVertex startingVertex, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Parameter validation if (quiver is null) { throw new ArgumentNullException(nameof(quiver)); } if (!quiver.Vertices.Contains(startingVertex)) { throw new ArgumentException($"The quiver does not contain the starting vertex {startingVertex}."); } if (transformationRuleTree is null) { throw new ArgumentNullException(nameof(transformationRuleTree)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } // Do search in the tree of all paths starting at startingVertex AnalysisStateForSingleStartingVertex <TVertex> state = DoSearchInPathTree(quiver, startingVertex, transformationRuleTree, settings); // Do cancellativity check bool shouldDoCancellativityFailureDetection = settings.DetectCancellativityFailure && !state.TooLongPathEncountered; bool cancellativityFailureDetected = shouldDoCancellativityFailureDetection ? DetectFailureOfCancellativity(state) : false; bool shouldDoWeakCancellativityFailureDetection = settings.DetectWeakCancellativityFailure && !state.TooLongPathEncountered; bool weakCancellativityFailureDetected = shouldDoWeakCancellativityFailureDetection ? DetectFailureOfWeakCancellativity(state) : false; var cancellativityFailuresDetected = CancellativityTypes.None; if (cancellativityFailureDetected) { cancellativityFailuresDetected |= CancellativityTypes.Cancellativity; } if (weakCancellativityFailureDetected) { cancellativityFailuresDetected |= CancellativityTypes.WeakCancellativity; } // Return results var results = new AnalysisResultsForSingleStartingVertex <TVertex>(state, cancellativityFailuresDetected); return(results); }
/// <summary> /// Determines the equivalence class of the specified node. /// </summary> /// <typeparam name="TVertex">The type of the vertices in the quiver.</typeparam> /// <param name="node">The node whose equivalence class to determine.</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><see cref="EquivalenceClassResult.TooLongPath"/> if the equivalence class has /// not been computed before, <paramref name="settings"/> has /// <see cref="AnalysisSettings.UseMaxLength"/> equal to <see langword="true"/>, and a path /// of length (in arrows) strictly greater than /// <see cref="AnalysisSettings.MaxPathLength"/> of <paramref name="settings"/> is /// encountered during the equivalence class search. Otherwise, /// <see cref="EquivalenceClassResult.Zero"/> if the equivalence class is the zero /// class, and <see cref="EquivalenceClassResult.Nonzero"/> if the equivalence class is /// not the zero class.</returns> /// <remarks> /// <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 DetermineEquivalenceClass <TVertex>( SearchTreeNode <TVertex> node, AnalysisStateForSingleStartingVertex <TVertex> state, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { if (node.EquivalenceClassIsComputed) { return(state.NodeIsZeroEquivalent(node) ? EquivalenceClassResult.Zero : EquivalenceClassResult.Nonzero); } return(ComputeEquivalenceClass(node, state, transformationRuleTree, settings)); }
public MaximalNonzeroEquivalenceClassRepresentativesResults <TVertex> ComputeMaximalNonzeroEquivalenceClassRepresentativesStartingAt <TVertex>( QuiverWithPotential <TVertex> qp, TVertex startingVertex, TransformationRuleTreeNode <TVertex> transformationRuleTree, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var bogusPath = new Path <TVertex>(qp.Quiver.Vertices.First()); var analysisResults = AnalyzeWithStartingVertex(qp, startingVertex, transformationRuleTree, settings); var outputResults = new MaximalNonzeroEquivalenceClassRepresentativesResults <TVertex>( analysisResults.NonCancellativityDetected ? CancellativityTypes.Cancellativity : CancellativityTypes.None, analysisResults.TooLongPathEncountered, analysisResults.MaximalPathRepresentatives.Select(node => node.Path), longestPathEncountered: bogusPath); // bogus path because this is an old method and the longest path feature is new return(outputResults); }
/// <summary> /// Creates a transformation rule tree for a <see cref="SemimonomialUnboundQuiver{TVertex}"/>. /// </summary> /// <typeparam name="TVertex">The type of the vertices of the quiver.</typeparam> /// <param name="unboundQuiver">The unbound quiver.</param> /// <returns>A transformation rule tree for <paramref name="semimonomialIdeal"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="semimonomialIdeal"/> is <see langword="null"/>.</exception> /// <exception cref="NotSupportedException"><paramref name="semimonomialIdeal"/> has two /// distinct non-monomial generators <c>p1 - q1</c> and <c>p2 - q2</c> where /// <c>p1, q1, p2, q2</c> are not all distinct.</exception> /// <exception cref="ArgumentException"><paramref name="semimonomialIdeal"/> has a /// non-monomial generator <c>p - q</c> where the paths <c>p</c> and <c>q</c> do not have /// the same endpoints.</exception> public TransformationRuleTreeNode <TVertex> CreateTransformationRuleTree <TVertex>(SemimonomialIdeal <TVertex> semimonomialIdeal) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var root = new TransformationRuleTreeNode <TVertex>(false, null, null); // Monomial generators (kill rules) foreach (var path in semimonomialIdeal.Paths) { var node = GetOrInsertDefaultNode(path, root); node.CanBeKilled = true; } // Non-monomial generators (replace rules) foreach (var difference in semimonomialIdeal.DifferencesOfPaths) { // Replacement rules where the paths have different starting or ending points are disallowed if (!difference.Minuend.StartingPoint.Equals(difference.Subtrahend.StartingPoint) || !difference.Minuend.EndingPoint.Equals(difference.Subtrahend.EndingPoint)) { throw new NotSupportedException("Ideals with a non-monomial generator whose two paths have different starting points or ending points are not supported."); } InsertReplacementRule(difference.Minuend, difference.Subtrahend); InsertReplacementRule(difference.Subtrahend, difference.Minuend); } void InsertReplacementRule(Path <TVertex> originalPath, Path <TVertex> replacementPath) { var node = GetOrInsertDefaultNode(originalPath, root); if (node.ReplacementPath != null) { // Shouldn't be too much work to add support for this imo throw new NotSupportedException("Ideals with a path occurring in more than one non-monomial generator are not supported."); } node.ReplacementPath = replacementPath; } return(root); }
/// <inheritdoc/> public MaximalNonzeroEquivalenceClassRepresentativesResults <TVertex> ComputeMaximalNonzeroEquivalenceClassRepresentativesStartingAt <TVertex>( Quiver <TVertex> quiver, TVertex startingVertex, TransformationRuleTreeNode <TVertex> transformationRuleTree, MaximalNonzeroEquivalenceClassRepresentativeComputationSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var analysisResults = AnalyzeWithStartingVertex(quiver, startingVertex, transformationRuleTree, settings); // The .ToList() call to eagerly evaluate the maximal path representatives is really // important here; otherwise, the MaximalPathRepresentatives property of the // MaximalNonzeroEquivalenceClassRepresentativesResult prevents the entire search tree // of analysisResults from being freed. var maximalPathRepresentatives = analysisResults.MaximalPathRepresentatives.Select(node => node.Path).ToList(); var outputResults = new MaximalNonzeroEquivalenceClassRepresentativesResults <TVertex>( analysisResults.CancellativityFailuresDetected, analysisResults.TooLongPathEncountered, maximalPathRepresentatives, analysisResults.LongestPathEncountered); return(outputResults); }
/// <remarks>This method should be made obsolete (prefer converting the QP to a /// semimonomial unbound quiver, and analyzing that instead (or at least creating the /// transformation rule tree for that instead).</remarks> public TransformationRuleTreeNode <TVertex> CreateTransformationRuleTree <TVertex>(QuiverWithPotential <TVertex> qp) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { if (qp is null) { throw new ArgumentNullException(nameof(qp)); } var root = new TransformationRuleTreeNode <TVertex>(false, null, null); foreach (var arrow in qp.Quiver.Arrows) { var linComb = new LinearCombination <Path <TVertex> >(); foreach (var(cycle, coefficient) in qp.Potential.LinearCombinationOfCycles.ElementToCoefficientDictionary) { linComb = linComb.Add(cycle.DifferentiateCyclically(arrow).Scale(coefficient)); } AddRulesFromSingleLinearCombination(linComb, root); } return(root); }
/// <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); }
/// <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); }
/// <summary> /// Gets a node according to a specified paths, inserting nodes without any transformation /// data into the tree as necessary along the way. /// </summary> /// <typeparam name="TVertex">The type of the vertices.</typeparam> /// <param name="path">The path according to which to get the node from the tree.</param> /// <param name="root">The root node of the tree.</param> /// <returns>The retrieved node.</returns> /// <remarks>Additional nodes without any transformation data are inserted into the tree as /// necessary between the root node and the node to insert.</remarks> private TransformationRuleTreeNode <TVertex> GetOrInsertDefaultNode <TVertex>(Path <TVertex> path, TransformationRuleTreeNode <TVertex> root) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var curNode = root; foreach (var vertex in path.Vertices.Reverse()) { if (!curNode.Children.ContainsKey(vertex)) { curNode.children[vertex] = new TransformationRuleTreeNode <TVertex>(false, null, curNode); } curNode = curNode.Children[vertex]; } return(curNode); }
private void AddRulesFromSingleLinearCombination <TVertex>(LinearCombination <Path <TVertex> > linComb, TransformationRuleTreeNode <TVertex> root) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { switch (linComb.ElementToCoefficientDictionary.Count) { case 0: return; case 1: var path = linComb.Elements.Single(); var node = GetOrInsertDefaultNode(path, root); node.CanBeKilled = true; break; case 2: var coefficients = linComb.ElementToCoefficientDictionary.Values.ToList(); var paths = linComb.Elements.ToList(); // Could be in different order from the coefficients, but don't care if (coefficients[1] == -coefficients[0]) { GetOrInsertDefaultNode(paths[0], root).ReplacementPath = paths[1]; GetOrInsertDefaultNode(paths[1], root).ReplacementPath = paths[0]; } else { throw new NotSupportedException("Linear combinations of length 2 with coefficients that are not the additive inverse of each other are not supported."); } break; default: throw new NotSupportedException("Linear combinations of length greater than 2 are not supported."); } }