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); }
public void AreIsomorphic_OnIsomorphicQuiversButNonIsomorphicPotentials_SameGroupSignaturesAndSameCounts() { var potential1 = new Potential <char>(new DetachedCycle <char>(new Path <char>('B', 'C', 'E', 'B')), +1) .AddCycle(new DetachedCycle <char>(new Path <char>('A', 'B', 'C', 'A')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('B', 'D', 'E', 'B')), -1); var potential2 = new Potential <char>(new DetachedCycle <char>(new Path <char>('B', 'C', 'E', 'B')), +1) .AddCycle(new DetachedCycle <char>(new Path <char>('A', 'B', 'C', 'A')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('C', 'E', 'F', 'C')), -1); var qp1 = new QuiverWithPotential <char>(potential1); var vertices2 = new char[] { 'A', 'B', 'C', 'D', 'E', 'F' }; var arrows2 = new Arrow <char>[] { new Arrow <char>('A', 'B'), new Arrow <char>('B', 'C'), new Arrow <char>('B', 'D'), new Arrow <char>('C', 'A'), new Arrow <char>('C', 'E'), new Arrow <char>('D', 'E'), new Arrow <char>('E', 'B'), new Arrow <char>('E', 'F'), new Arrow <char>('F', 'C'), }; var quiver2 = new Quiver <char>(vertices2, arrows2); var qp2 = new QuiverWithPotential <char>(quiver2, potential2); var checker = new QPIsomorphismChecker(); Assert.That(checker.AreIsomorphic(qp1, qp2), Is.False); }
public void Analyze_DoesNotTryToUnionTheSameClasses_2() { var potential = new Potential <int>(new Dictionary <DetachedCycle <int>, int> { { new DetachedCycle <int>(0, 1, 2, 3, 4, 0), +1 }, { new DetachedCycle <int>(0, 5, 4, 0), -1 }, { new DetachedCycle <int>(3, 4, 6, 3), -1 }, { new DetachedCycle <int>(2, 3, 7, 2), -1 }, { new DetachedCycle <int>(1, 2, 8, 1), -1 }, { new DetachedCycle <int>(0, 1, 9, 0), -1 }, { new DetachedCycle <int>(0, 5, 10, 0), +1 }, { new DetachedCycle <int>(4, 6, 11, 4), +1 }, { new DetachedCycle <int>(3, 7, 12, 3), +1 }, { new DetachedCycle <int>(2, 8, 13, 2), +1 }, { new DetachedCycle <int>(1, 9, 14, 1), +1 }, { new DetachedCycle <int>(0, 15, 5, 10, 0), -1 }, { new DetachedCycle <int>(4, 16, 6, 11, 4), -1 }, { new DetachedCycle <int>(3, 17, 7, 12, 3), -1 }, { new DetachedCycle <int>(2, 18, 8, 13, 2), -1 }, { new DetachedCycle <int>(1, 19, 9, 14, 1), -1 } }); var qp = new QuiverWithPotential <int>(potential); var analyzer = CreateAnalyzer(); var settings = CreateSettings(maxPathLength: 20, EarlyTerminationConditions.None); Assert.That(() => analyzer.Analyze(qp, settings), Throws.Nothing); }
/// <summary> /// Analyzes a QP with the specified computer of maximal nonzero equivalence class /// representatives. /// </summary> /// <typeparam name="TVertex">The type of the vertices of the quiver.</typeparam> /// <param name="qp">The quiver with potential.</param> /// <param name="settings">The settings for the analysis.</param> /// <param name="computer">A computer of maximal nonzero equivalence class representatives.</param> /// <returns>The results of the analysis.</returns> /// <exception cref="ArgumentNullException"><paramref name="qp"/> is /// <see langword="null"/>, or <paramref name="settings"/> is <see langword="null"/>, /// or <paramref name="computer"/> is <see langword="null"/>.</exception> /// <exception cref="NotSupportedException">The potential of <paramref name="qp"/> has a /// cycle with coefficient not equal to either of -1 and +1, /// or some arrow occurs multiple times in a single cycle of the potential of /// <paramref name="qp"/>.</exception> /// <exception cref="ArgumentException">For some arrow in the potential of /// <paramref name="qp"/> and sign, the arrow is contained in more than one cycle of that /// sign.</exception> public IQPAnalysisResults <TVertex> Analyze <TVertex>( QuiverWithPotential <TVertex> qp, QPAnalysisSettings settings, IMaximalNonzeroEquivalenceClassRepresentativeComputer computer) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { if (qp is null) { throw new ArgumentNullException(nameof(qp)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } if (computer is null) { throw new ArgumentNullException(nameof(computer)); } // Simply get the underlying semimonomial unbound quiver and analyze it using the appropriate analyzer var semimonomialUnboundQuiver = SemimonomialUnboundQuiverFactory.CreateSemimonomialUnboundQuiverFromQP(qp); var suqAnalyzer = new SemimonomialUnboundQuiverAnalyzer(); var suqSettings = AnalysisSettingsFactory.CreateSemimonomialUnboundQuiverAnalysisSettings(settings); var suqResults = suqAnalyzer.Analyze(semimonomialUnboundQuiver, suqSettings, computer); var results = AnalysisResultsFactory.CreateQPAnalysisResults(suqResults); return(results); }
private void AssertIsSelfInjective <TVertex>(QuiverWithPotential <TVertex> qp) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var analyzer = new QPAnalyzer(); var result = analyzer.Analyze(qp, new QPAnalysisSettings(CancellativityTypes.Cancellativity)); Assert.That(result.MainResults.IndicatesSelfInjectivity()); }
public void AreIsomorphic_SmallQP1() { var potential1 = new Potential <int>(new DetachedCycle <int>(new Path <int>(1, 2, 3, 1)), +1); var potential2 = new Potential <int>(new DetachedCycle <int>(new Path <int>(3, 2, 1, 3)), +1); var qp1 = new QuiverWithPotential <int>(potential1); var qp2 = new QuiverWithPotential <int>(potential2); var checker = new QPIsomorphismChecker(); Assert.That(checker.AreIsomorphic(qp1, qp2), Is.True); }
// 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); }
public void Deconstruct( out LayerType layerType, out IEnumerable <Composition> compositions, out QuiverInPlane <int> quiverInPlane, out QuiverWithPotential <int> qp) { layerType = LayerType; compositions = Compositions; quiverInPlane = QuiverInPlane; qp = QP; }
/// <summary> /// Initializes a new instance of the <see cref="InteractiveLayeredQuiverGeneratorOutput"/> /// class. /// </summary> /// <param name="layerType">The layer type of the layered quiver.</param> /// <param name="compositions">The compositions that specify the arrows of the layered quiver.</param> /// <param name="quiverInPlane">A <see cref="QuiverInPlane{TVertex}"/> representing the /// layered quiver.</param> /// <param name="qp">A <see cref="QuiverWithPotential{TVertex}"/> representing the layered /// quiver.</param> public InteractiveLayeredQuiverGeneratorOutput( LayerType layerType, IEnumerable <Composition> compositions, QuiverInPlane <int> quiverInPlane, QuiverWithPotential <int> qp) { LayerType = layerType ?? throw new ArgumentNullException(nameof(layerType)); Compositions = compositions ?? throw new ArgumentNullException(nameof(compositions)); QuiverInPlane = quiverInPlane ?? throw new ArgumentNullException(nameof(quiverInPlane)); QP = qp ?? throw new ArgumentNullException(nameof(qp)); }
public void AreIsomorphic_SmallQP4() { var potential1 = new Potential <int>(new DetachedCycle <int>(new Path <int>(1, 2, 3, 1)), +1) .AddCycle(new DetachedCycle <int>(new Path <int>(4, 5, 6, 4)), +1); var potential2 = new Potential <int>(new DetachedCycle <int>(new Path <int>(3, 2, 1, 3)), +1); var qp1 = new QuiverWithPotential <int>(potential1); var quiver2 = qp1.Quiver; var qp2 = new QuiverWithPotential <int>(quiver2, potential2); var checker = new QPIsomorphismChecker(); Assert.That(checker.AreIsomorphic(qp2, qp1), Is.False); }
// Sets up variables for a call to Analyzer.ComputeMaximalNonzeroEquivalenceClassRepresentativesStartingAt (and possibly other methods) private static void DoSetup( QuiverWithPotential <int> qp, out QPAnalyzer analyzer, out TransformationRuleTreeNode <int> ruleTree, out QPAnalysisSettings settings) { analyzer = new QPAnalyzer(); var ruleCreator = new TransformationRuleTreeCreator(); ruleTree = ruleCreator.CreateTransformationRuleTree(qp); settings = CreateSettings(detectNonCancellativity: false); }
/// <summary> /// Analyzes a QP in a way that utilizes the "periodicity" of the QP and /// concurrently. /// </summary> /// <typeparam name="TVertex">The type of the vertices of the quiver.</typeparam> /// <param name="qp">The quiver with potential.</param> /// <param name="periods">A collection of consecutive non-empty periods of the QP that are /// jointly exhaustive and mutually exclusive.</param> /// <param name="settings">The settings for the analysis.</param> /// <returns>The results of the analysis.</returns> /// <exception cref="ArgumentNullException"><paramref name="qp"/> is /// <see langword="null"/>, /// or <paramref name="periods"/> is <see langword="null"/>, /// or <paramref name="settings"/> is <see langword="null"/>.</exception> /// <exception cref="NotSupportedException">The potential of <paramref name="qp"/> has a /// cycle with coefficient not equal to either of -1 and +1, /// or some arrow occurs multiple times in a single cycle of the potential of /// <paramref name="qp"/>.</exception> /// <exception cref="ArgumentException">For some arrow in the potential of /// <paramref name="qp"/> and sign, the arrow is contained in more than one cycle of that /// sign, or some of the periods in <paramref name="periods"/> overlap, or the union of all /// periods in <paramref name="periods"/> is not precisely the collection of all vertices /// in the quiver.</exception> /// <remarks> /// <para>Some validation of <paramref name="periods"/> is done, but /// <paramref name="periods"/> is not verified to constitute a sequence of consecutive /// "periods" of the QP.</para> /// </remarks> public IQPAnalysisResults <TVertex> AnalyzeUtilizingPeriodicityConcurrently <TVertex>( QuiverWithPotential <TVertex> qp, IEnumerable <IEnumerable <TVertex> > periods, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { return(AnalyzeUtilizingPeriodicityConcurrently( qp, periods, settings, defaultComputer)); }
/// <summary> /// Prompts the user for a QP. /// </summary> /// <param name="qp">Output parameter for the QP.</param> /// <param name="periods">Output parameter for the periods of the QP.</param> /// <param name="fixedPoint">Output parameter for the fixed-point of the QP (or /// <see langword="null"/> if there is none).</param> /// <returns><see langword="true"/> if the user specified a QP; /// <see langword="false"/> otherwise.</returns> private bool TryGetQP( out QuiverWithPotential <int> qp, out IEnumerable <IEnumerable <int> > periods, out int?fixedPoint) { if (!TryGetQPType(out var qpType) || !TryGetNumPeriods(qpType, out int numPeriods)) { qp = null; periods = null; fixedPoint = null; return(false); } switch (qpType) { case QPType.OddFlower: qp = UsefulQPs.GetOddFlowerQP(numPeriods, DefaultFirstVertex); periods = UsefulQPs.GetPeriodsOfOddFlowerQP(numPeriods, DefaultFirstVertex); fixedPoint = null; break; case QPType.EvenFlowerType1: qp = UsefulQPs.GetEvenFlowerType1QP(numPeriods, DefaultFirstVertex); periods = UsefulQPs.GetPeriodsOfEvenFlowerType1QP(numPeriods, DefaultFirstVertex); fixedPoint = null; break; case QPType.EvenFlowerType2: qp = UsefulQPs.GetEvenFlowerType2QP(numPeriods, DefaultFirstVertex); periods = UsefulQPs.GetPeriodsOfEvenFlowerType2QP(numPeriods, DefaultFirstVertex); fixedPoint = null; break; case QPType.PointedFlower: qp = UsefulQPs.GetPointedFlowerQP(numPeriods, DefaultFirstVertex); periods = UsefulQPs.GetPeriodsOfPointedFlowerQPWithoutFixedPoint(numPeriods, DefaultFirstVertex); throw new NotImplementedException(); break; default: Debug.Fail($"Invalid QP type ({qpType}) specified."); qp = null; periods = null; fixedPoint = null; return(false); } return(true); }
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); }
public void AreIsomorphic_OnTriangleQPs() { var potential1 = new Potential <char>(new DetachedCycle <char>(new Path <char>('B', 'C', 'E', 'B')), +1) .AddCycle(new DetachedCycle <char>(new Path <char>('A', 'B', 'C', 'A')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('B', 'D', 'E', 'B')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('C', 'E', 'F', 'C')), -1); var potential2 = new Potential <char>(new DetachedCycle <char>(new Path <char>('B', 'C', 'F', 'B')), +1) .AddCycle(new DetachedCycle <char>(new Path <char>('C', 'F', 'E', 'C')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('B', 'C', 'D', 'B')), -1) .AddCycle(new DetachedCycle <char>(new Path <char>('A', 'F', 'B', 'A')), -1); var qp1 = new QuiverWithPotential <char>(potential1); var qp2 = new QuiverWithPotential <char>(potential2); var checker = new QPIsomorphismChecker(); Assert.That(checker.AreIsomorphic(qp1, qp2), Is.True); }
public void TryExtractQP_WorksOnClockwiseCycle(int cycleLength) { const double Radius = 1000; var vertices = Enumerable.Range(1, cycleLength); var arrows = vertices.Select(k => new Arrow <int>(k, k.Modulo(cycleLength) + 1)); double baseAngle = 2 * Math.PI / cycleLength; var vertexPositions = vertices.ToDictionary(k => k, k => new Point((int)(Radius * Math.Cos(-k * baseAngle)), (int)Math.Round(Radius * Math.Sin(-k * baseAngle)))); var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositions); var extractor = CreateQPExtractor(); var result = extractor.TryExtractQP(quiverInPlane, out var qp); var expectedQuiver = new Quiver <int>(vertices, arrows); var expectedPotential = new Potential <int>(new DetachedCycle <int>(vertices.AppendElement(1)), +1); var expectedQP = new QuiverWithPotential <int>(expectedQuiver, expectedPotential); Assert.That(result, Is.EqualTo(QPExtractionResult.Success)); Assert.That(qp, Is.EqualTo(expectedQP)); }
/// <remarks>Like <see cref="TryExecuteRecipe(PotentialRecipe, int, out QuiverWithPotential{int})"/> /// but with a boolean parameter for throwing.</remarks> private bool InternalExecuteRecipe(PotentialRecipe recipe, int initialCycleNumArrows, out QuiverWithPotential <int> qp, bool shouldThrow) { if (recipe == null) { throw new ArgumentNullException(nameof(recipe)); } if (initialCycleNumArrows <= 0) { throw new ArgumentOutOfRangeException(nameof(initialCycleNumArrows)); } qp = null; var startCycle = Utility.MakeCycle(0, initialCycleNumArrows); var startArrows = startCycle.CanonicalPath.Arrows; var boundaryTuples = startArrows.Select(a => (a, BoundaryArrowOrientation.Right)); var state = new RecipeExecutorState { Potential = new Potential <int>(startCycle, +1), NextVertex = initialCycleNumArrows, Boundary = new CircularList <(Arrow <int>, BoundaryArrowOrientation)>(boundaryTuples), PotentiallyProblematicArrows = new HashSet <Arrow <int> >(), }; foreach (var instruction in recipe.Instructions) { if (shouldThrow) { state = instruction.Execute(state); } else if (!instruction.TryExecute(state, out state)) { return(false); } } qp = new QuiverWithPotential <int>(state.Potential); return(true); }
public QPExtractionResult TryExtractQP <TVertex>(QuiverInPlane <TVertex> quiverInPlane, out QuiverWithPotential <TVertex> qp) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { if (quiverInPlane is null) { throw new ArgumentNullException(nameof(quiverInPlane)); } // Not entirely sure that loops are a catastrophy if (quiverInPlane.HasLoops()) { qp = null; return(QPExtractionResult.QuiverHasLoops); } if (quiverInPlane.HasAntiParallelArrows()) { qp = null; return(QPExtractionResult.QuiverHasAntiParallelArrows); } // Algorithm: // 1. Perform a "face search" on the underlying graph, the output of which is a // collection of faces, each described by its bounding cycle (of non-directed edges) // 2. For every face, try to get its orientation in the directed quiver: // If the bounding cycle or its reverse is a path in the directed quiver, // determine the orientation (counterclockwise or clockwise). // Else, return "QuiverHasFaceWithInconsistentOrientation" // 3. Construct the potential with positive cycles the cycles from step (2) with // clockwise orientation and with negative cycles the cycles from step (2) with // counterclockwise orientation // 4. Output the QP and return Success. var underlyingGraph = quiverInPlane.GetUnderlyingGraph(); var faceFinder = new FaceFinder(); // Remember that this outputs counterclockwise bounding cycles. if (!faceFinder.TryFindFaces(underlyingGraph, out var boundingCyclesAsVertexCollections)) { qp = null; return(QPExtractionResult.QuiverIsNotPlane); } var boundingCycles = boundingCyclesAsVertexCollections.Select(cycleAsVertexInPlaneCollection => cycleAsVertexInPlaneCollection.Select(vertexInPlane => vertexInPlane.Vertex)) .Select(cycleAsVertexCollection => new DetachedCycle <TVertex>(cycleAsVertexCollection)); var counterclockwiseFaceCycles = new List <DetachedCycle <TVertex> >(); var clockwiseFaceCycles = new List <DetachedCycle <TVertex> >(); foreach (var cycle in boundingCycles) { // Counterclockwise bounding cycle in the directed quiver (and so on) if (cycle.CanonicalPath.Arrows.All(arrow => quiverInPlane.ContainsArrow(arrow))) { counterclockwiseFaceCycles.Add(cycle); } else if (cycle.Reverse().CanonicalPath.Arrows.All(arrow => quiverInPlane.ContainsArrow(arrow))) { clockwiseFaceCycles.Add(cycle.Reverse()); } else { qp = null; return(QPExtractionResult.QuiverHasFaceWithInconsistentOrientation); } } var counterclockwiseDict = counterclockwiseFaceCycles.ToDictionary(cycle => cycle, cycle => - 1); var clockwiseDict = clockwiseFaceCycles.ToDictionary(cycle => cycle, cycle => + 1); var dict = counterclockwiseDict.Concat(clockwiseDict).ToDictionary(p => p.Key, p => p.Value); var potential = new Potential <TVertex>(dict); qp = new QuiverWithPotential <TVertex>(quiverInPlane.GetUnderlyingQuiver(), potential); return(QPExtractionResult.Success); //// Algorithm: //// Order the arrows from a vertex cyclically by the angle, say with a dictionary of cyclic lists: //// vertex -> [target1, target2, ...] //// //// Because clockwise gets positive sign, it might be easier to order things cyclically by -angle //// //// Keep track of signed arrows "consumed" (the sign corresponding to turning left or right) //// //// While there is an arrow with a non-consumed sign (a non-consumed signed arrow, say): //// Do a 'left/right (depending on the non-consumed sign) face search' for the arrow //// //// How a face search is done is documented elsewhere for now. //// //// This produces a collection of positively oriented faces (represented by the bounding cycle) //// and a collection of negatively oriented faces (represented by the bounding cycle) //// //// Take the bounding cycles of the positively oriented faces as the cycles in the potential with positive sign //// and the bounding cycles of the negatively oriented faces as the cycles in the potential with negative sign //var counterclockwiseFaceCycles = SearchForFaces(Orientation.Counterclockwise); //var clockwiseFaceCycles = SearchForFaces(Orientation.Clockwise); //var leftDict = counterclockwiseFaceCycles.ToDictionary(cycle => cycle, cycle => -1); //var rightDict = clockwiseFaceCycles.ToDictionary(cycle => cycle, cycle => +1); //var dict = counterclockwiseDict.Concat(clockwiseDict).ToDictionary(p => p.Key, p => p.Value); //var potential = new Potential<TVertex>(dict); //qp = new QuiverWithPotential<TVertex>(quiverInPlane.GetUnderlyingQuiver(), potential); //return QPExtractorResult.Success; //IEnumerable<DetachedCycle<TVertex>> SearchForFaces(Orientation searchOrientation) //{ // var cycles = new HashSet<DetachedCycle<TVertex>>(); // var remainingArrows = new HashSet<Arrow<TVertex>>(quiverInPlane.GetArrows()); // var remainingArrowsStack = new Stack<Arrow<TVertex>>(quiverInPlane.GetArrows()); // while (remainingArrows.Count > 0) // { // Arrow<TVertex> startArrow; // while (!remainingArrows.Contains(startArrow = remainingArrowsStack.Pop())) ; // var path = new Path<TVertex>(startArrow.Source, startArrow.Target); // var prevVertex = startArrow.Source; // var curVertex = startArrow.Target; // // Start the search for this start arrow! // while (true) // { // // Terminate the search if we have found a cycle // if (path.TryExtractTrailingCycle(out var closedPath, out int startIndex)) // { // var cycleOrientation = quiverInPlane.GetOrientationOfCycle(closedPath); // // If the orientations agree, then the cycle is a bounding cycle for a (bounded) face // // Else, it bounds the unbounded face (which we do not care about) // if (cycleOrientation == searchOrientation) // { // var detachedCycle = new DetachedCycle<TVertex>(closedPath); // // When restarting with a boundary arrow, we might get the same cycle again // if (!cycles.Contains(detachedCycle)) cycles.Add(new DetachedCycle<TVertex>(closedPath)); // } // foreach (var arrow in path.Arrows) remainingArrows.Remove(arrow); // break; // } // // Get next successor (next in the angle sense) // // If no successor, terminate the search (all arrows are direction-boundary arrows) // // Else, update path, prevVertex and curVertex and do next iteration // var successors = quiverInPlane.AdjacencyLists[curVertex]; // if (successors.Count == 0) // { // foreach (var arrow in path.Arrows) remainingArrows.Remove(arrow); // break; // } // var baseVertex = curVertex; // var basePos = quiverInPlane.GetVertexPosition(baseVertex); // var successorsAndPredecessor = successors.AppendElement(prevVertex); // // Sort the vertices by angle so that the vertex following the predecessor vertex (prevVertex) is // // the vertex corresponding to a "maximal" turn. // var successorsAndPredecessorSortedByAngle = searchOrientation == Orientation.Clockwise ? // successorsAndPredecessor.OrderBy(vertex => quiverInPlane.GetVertexPosition(vertex), new AngleBasedPointComparer(basePos)) : // successorsAndPredecessor.OrderByDescending(vertex => quiverInPlane.GetVertexPosition(vertex), new AngleBasedPointComparer(basePos)); // var successorsAndPredecessorSortedByAngleList = new CircularList<TVertex>(successorsAndPredecessorSortedByAngle); // int predecessorIndex = successorsAndPredecessorSortedByAngleList.IndexOf(prevVertex); // successorsAndPredecessorSortedByAngleList.RotateLeft(predecessorIndex); // var nextVertex = successorsAndPredecessorSortedByAngleList.Skip(1).First(); // path = path.AppendVertex(nextVertex); // prevVertex = curVertex; // curVertex = nextVertex; // } // } // return cycles; //} //var successorsSortedByAngle = quiverInPlane.Vertices.Select(baseVertex => //{ // var basePos = quiverInPlane.GetVertexPosition(baseVertex); // var sortedSuccessors = quiverInPlane.AdjacencyLists[baseVertex].OrderBy(vertex => quiverInPlane.GetVertexPosition(vertex), new AngleBasedPointComparer(basePos)); // var sortedVertexList = new CircularList<TVertex>(sortedSuccessors); // return new KeyValuePair<TVertex, CircularList<TVertex>>(baseVertex, sortedVertexList); //}).ToDictionary(p => p.Key, p => p.Value); //var arrows = new HashSet<Arrow<TVertex>>(quiverInPlane.GetArrows()); ////TODO: Continue here //throw new NotImplementedException(); //// Old idea //// Algorithm: //// Order the arrows to/from a vertex cyclically by the angle, say with a dictionary of cyclic lists of tuples: //// vertex -> [(outgoing/incoming, target/source), ...] //// //// Because clockwise gets positive sign, it might be easier to order things cyclically by -angle //// //// Keep track of arrows "consumed" and their signs //// //// For every vertex: //// Begin with the first outgoing arrow //// Check if the next arrow is incoming //// If not, //// Try to "turn right" (take the previous arrow) //// ////var neighborsSortedByAngle = quiverInPlane.Vertices.Select(baseVertex => ////{ //// var basePos = quiverInPlane.GetVertexPosition(baseVertex); //// var outgoingArrowTargetVertices = quiverInPlane.AdjacencyLists[baseVertex]; //// var incomingArrowSourceVertices = quiverInPlane.Vertices.Where(sourceVertex => quiverInPlane.AdjacencyLists[sourceVertex].Contains(baseVertex)); //// var undirectedlyAdjacentVertices = incomingArrowSourceVertices.Concat(outgoingArrowTargetVertices); //// var sortedVertices = undirectedlyAdjacentVertices.OrderBy(vertex => quiverInPlane.GetVertexPosition(vertex), new AngleBasedPointComparer(basePos)); //// var sortedVertexList = new CircularList<TVertex>(sortedVertices); //// return new KeyValuePair<TVertex, CircularList<TVertex>>(baseVertex, sortedVertexList); ////}).ToDictionary(p => p.Key, p => p.Value); ////var arrows = new HashSet<Arrow<TVertex>>(quiverInPlane.GetArrows()); ////TODO: Continue here ////throw new NotImplementedException(); }
/// <summary> /// Analyzes a QP with the default computer of maximal nonzero equivalence class /// representatives. /// </summary> /// <typeparam name="TVertex">The type of the vertices of the quiver.</typeparam> /// <param name="qp">The quiver with potential.</param> /// <param name="settings">The settings for the analysis.</param> /// <returns>The results of the analysis.</returns> /// <exception cref="ArgumentNullException"><paramref name="qp"/> is /// <see langword="null"/>, or <paramref name="settings"/> is /// <see langword="null"/>.</exception> /// <exception cref="NotSupportedException">The potential of <paramref name="qp"/> has a /// cycle with coefficient not equal to either of -1 and +1, /// or some arrow occurs multiple times in a single cycle of the potential of /// <paramref name="qp"/>.</exception> /// <exception cref="ArgumentException">For some arrow in the potential of /// <paramref name="qp"/> and sign, the arrow is contained in more than one cycle of that /// sign.</exception> public IQPAnalysisResults <TVertex> Analyze <TVertex>(QuiverWithPotential <TVertex> qp, QPAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { return(Analyze(qp, settings, defaultComputer)); }
/// <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); }
private void DoSetup(QuiverWithPotential <int> qp, out TransformationRuleTreeNode <int> ruleTree) { var creator = new TransformationRuleTreeCreator(); ruleTree = creator.CreateTransformationRuleTree(qp); }
/// <summary> /// Executes the specified recipe. /// </summary> /// <param name="recipe">The recipe according to which to generate the potential of the QP.</param> /// <param name="initialCycleNumArrows">The number of arrows in the first cycle.</param> /// <param name="qp">Output parameter for the QP generated according to the recipe. If the /// recipe is not executed successfully, the output value is <see langword="null"/>.</param> /// <returns>A boolean value indicating whether the recipe was executed successfully.</returns> public bool TryExecuteRecipe(PotentialRecipe recipe, int initialCycleNumArrows, out QuiverWithPotential <int> qp) { return(InternalExecuteRecipe(recipe, initialCycleNumArrows, out qp, false)); }