public QuiverWithPotential(Quiver <TVertex> quiver, Potential <TVertex> potential) { if (quiver == null) { throw new ArgumentNullException(nameof(quiver)); } if (potential == null) { throw new ArgumentNullException(nameof(potential)); } foreach (var cycle in potential.Cycles) { if (!quiver.Vertices.Contains(cycle.CanonicalPath.StartingPoint)) { throw new ArgumentException($"The starting point {cycle.CanonicalPath.StartingPoint} of the canonical path of one of the cycles in the potential is not a vertex in the quiver."); } foreach (var arrow in cycle.CanonicalPath.Arrows.Skip(1)) { if (!quiver.Vertices.Contains(arrow.Source)) { throw new ArgumentException(String.Format("The vertex {0} is present in one of the cycles in the potential but is not a vertex in the quiver.", arrow.Source)); } } } Quiver = quiver; Potential = potential; }
public SelfInjectiveQP <int> GetSelfInjectiveTriangleQP(int numRows, int firstVertex = DefaultFirstVertex) { if (numRows < 2) { throw new ArgumentOutOfRangeException(nameof(numRows)); } var qp = UsefulQPs.GetTriangleQP(numRows); var potential = new Potential <int>(); // Rotation "once" clockwise to get the Nakayama permutation var nakayamaPermutation = new Dictionary <int, int>(); // Start with the right-most "column" (1, 3, 6, 10, ...), which is mapped to the bottom row, // and go left to (2, 5, 9, ...), which is mapped to the second last row, and so on. var columnVertices = Enumerable.Range(1, numRows).Select(k => Utility.TriangularNumber(k)); for (int rowIndex = numRows; rowIndex >= 1; rowIndex--) { var rowVertices = Enumerable.Range(Utility.TriangularNumber(rowIndex - 1) + 1, rowIndex).Reverse(); var inputOutputPairs = columnVertices.Zip(rowVertices, (x, y) => (x, y)); foreach (var(x, y) in inputOutputPairs) { nakayamaPermutation[x] = y; } columnVertices = columnVertices.Select(x => x - 1).Skip(1); } return(new SelfInjectiveQP <int>(qp, nakayamaPermutation)); }
/// <summary> /// Gets the "classic" non-cancellative QP, which is not even weakly cancellative. /// </summary> /// <returns>The "classic" non-cancellative QP.</returns> public static QuiverWithPotential <int> GetClassicNonCancellativeQP() { var potential = new Potential <int>(); potential = potential.AddCycle(new DetachedCycle <int>(1, 2, 4, 5, 1), -1); potential = potential.AddCycle(new DetachedCycle <int>(1, 3, 4, 5, 1), +1); var qp = new QuiverWithPotential <int>(potential); return(qp); }
public static QuiverWithPotential <int> GetCobwebQP(int numVerticesInCenterPolygon, int firstVertex = DefaultFirstVertex) { if (!CobwebParameterIsValid(numVerticesInCenterPolygon)) { throw new ArgumentOutOfRangeException(nameof(numVerticesInCenterPolygon)); } int numLayers = (numVerticesInCenterPolygon - 1) / 2; int numVerticesInFullLayer = 2 * numVerticesInCenterPolygon; var potential = new Potential <int>(); // Center polygon potential = potential.AddCycle(new DetachedCycle <int>(Enumerable.Range(firstVertex, numVerticesInCenterPolygon).AppendElement(firstVertex)), +1); if (numLayers > 1) { // First layer (the squares and triangles between the first and second layers) var curLayer = GetLayerVertices(0); var nextLayer = GetLayerVertices(1); for (int indexInLayer = 0; indexInLayer < numVerticesInCenterPolygon; indexInLayer++) { var triangleVertices = new int[] { curLayer[indexInLayer], nextLayer[2 * indexInLayer - 1], nextLayer[2 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(triangleVertices), +1); var squareVertices = new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[2 * indexInLayer + 1], nextLayer[2 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), -1); } // Remaining layers (only squares) for (int layerIndex = 1; layerIndex < numLayers - 1; layerIndex++) // 0-based layer index { curLayer = GetLayerVertices(layerIndex); nextLayer = GetLayerVertices(layerIndex + 1); for (int indexInLayer = 0; indexInLayer < numVerticesInFullLayer; indexInLayer++) { int sign = (layerIndex + indexInLayer).Modulo(2) == 1 ? +1 : -1; var squareVertices = sign == +1 ? new int[] { curLayer[indexInLayer + 1], curLayer[indexInLayer], nextLayer[indexInLayer], nextLayer[indexInLayer + 1], curLayer[indexInLayer + 1] } : new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[indexInLayer + 1], nextLayer[indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), sign); } } } var qp = new QuiverWithPotential <int>(potential); return(qp); CircularList <int> GetLayerVertices(int layerIndex) => new CircularList <int>(GetVerticesInCobwebQPLayer(numVerticesInCenterPolygon, layerIndex, firstVertex)); }
public QuiverWithPotential(Potential <TVertex> potential) { if (potential == null) { throw new ArgumentNullException(nameof(potential)); } var vertices = new HashSet <TVertex>(potential.Cycles.SelectMany(c => c.CanonicalPath.Vertices)); var arrows = new HashSet <Arrow <TVertex> >(potential.Cycles.SelectMany(c => c.CanonicalPath.Arrows)); var quiver = new Quiver <TVertex>(vertices, arrows); Quiver = quiver; Potential = potential; }
public static QuiverWithPotential <int> GetTriangleQP(int numRows, int firstVertex = DefaultFirstVertex) { if (!TriangleParameterIsValid(numRows)) { throw new ArgumentOutOfRangeException(nameof(numRows)); } if (numRows == 1) { var quiver = UsefulQuivers.GetTriangleQuiver(numRows, firstVertex); return(new QuiverWithPotential <int>(quiver, new Potential <int>())); } var potential = new Potential <int>(); var curRowVertices = new List <int>() { firstVertex }; int nextVertex = firstVertex + 1; for (int rowIndex = 1; rowIndex <= numRows - 1; rowIndex++) // 1-based row index { var nextRowVertices = Enumerable.Range(nextVertex, rowIndex + 1).ToList(); nextVertex += rowIndex + 1; // (2*rowIndex - 1) triangles to add. Draw a small triangle QP to realize that // the cycles below are the ones to add. for (int indexInRow = 0; indexInRow < rowIndex; indexInRow++) { var cycleVertices = new int[] { curRowVertices[indexInRow], nextRowVertices[indexInRow + 1], nextRowVertices[indexInRow], curRowVertices[indexInRow] }; potential = potential.AddCycle(new DetachedCycle <int>(cycleVertices), +1); // Positive for clockwise cycles. } for (int indexInRow = 0; indexInRow < rowIndex - 1; indexInRow++) { var cycleVertices = new int[] { curRowVertices[indexInRow + 1], curRowVertices[indexInRow], nextRowVertices[indexInRow + 1], curRowVertices[indexInRow + 1] }; potential = potential.AddCycle(new DetachedCycle <int>(cycleVertices), -1); // Negative for counterclockwise cycles. } curRowVertices = nextRowVertices; } var qp = new QuiverWithPotential <int>(potential); return(qp); }
/// <summary> /// /// </summary> /// <param name="numRows">The number of rows of vertices.</param> /// <param name="firstVertex"></param> /// <returns></returns> public static QuiverWithPotential <int> GetSquareQP(int numRows, int firstVertex = DefaultFirstVertex) { if (!SquareParameterIsValid(numRows)) { throw new ArgumentOutOfRangeException(nameof(numRows)); } int numVerticesInRow = numRows; int numVertices = numVerticesInRow * numRows; if (numRows == 1) { var quiver = UsefulQuivers.GetSquareQuiver(numRows, firstVertex); return(new QuiverWithPotential <int>(quiver, new Potential <int>())); } var potential = new Potential <int>(); for (int rowIndex = 0; rowIndex < numRows - 1; rowIndex++) { var curRow = GetVerticesInSquareQPRow(numRows, rowIndex, firstVertex).ToList(); var nextRow = GetVerticesInSquareQPRow(numRows, rowIndex + 1, firstVertex).ToList(); for (int indexInRow = 0; indexInRow < numVerticesInRow - 1; indexInRow++) { int sign = (rowIndex + indexInRow).Modulo(2) == 0 ? +1 : -1; var cycleVertices = sign == +1 ? new int[] { curRow[indexInRow], curRow[indexInRow + 1], nextRow[indexInRow + 1], nextRow[indexInRow], curRow[indexInRow] } : new int[] { curRow[indexInRow + 1], curRow[indexInRow], nextRow[indexInRow], nextRow[indexInRow + 1], curRow[indexInRow + 1] }; potential = potential.AddCycle(new DetachedCycle <int>(cycleVertices), sign); } } var qp = new QuiverWithPotential <int>(potential); return(qp); }
public static QuiverWithPotential <int> GetPointedFlowerQP(int numPeriods, int firstVertex = DefaultFirstVertex) { if (!PointedFlowerParameterIsValid(numPeriods)) { throw new ArgumentOutOfRangeException(nameof(numPeriods)); } int numLayers = UsefulQuivers.GetNumberOfLayersInPointedFlowerQuiver(numPeriods); int numVerticesInFullInnerLayer = 2 * numPeriods; int numVerticesInOuterLayer = 3 * numPeriods; var potential = new Potential <int>(); if (numLayers == 2) { int centerVertex = GetLayerVertices(0).Single(); var nextLayer = GetLayerVertices(1); for (int periodIndex = 0; periodIndex < numPeriods; periodIndex++) { int indexInNextLayer = 3 * periodIndex; // Square var positiveCycleVertices = new int[] { centerVertex, nextLayer[indexInNextLayer], nextLayer[indexInNextLayer + 1], nextLayer[indexInNextLayer + 2], centerVertex }; // Triangle var negativeCycleVertices = new int[] { centerVertex, nextLayer[indexInNextLayer + 3], nextLayer[indexInNextLayer + 2], centerVertex }; potential = potential.AddCycle(new DetachedCycle <int>(positiveCycleVertices), +1); potential = potential.AddCycle(new DetachedCycle <int>(negativeCycleVertices), -1); } return(new QuiverWithPotential <int>(potential)); } // Polygons between first and second layer { int centerVertex = GetLayerVertices(0).Single(); var nextLayer = GetLayerVertices(1); for (int periodIndex = 0; periodIndex < numPeriods; periodIndex++) { int indexInNextLayer = 2 * periodIndex; // Triangles var positiveCycleVertices = new int[] { centerVertex, nextLayer[indexInNextLayer], nextLayer[indexInNextLayer + 1], centerVertex }; var negativeCycleVertices = new int[] { centerVertex, nextLayer[indexInNextLayer + 2], nextLayer[indexInNextLayer + 1], centerVertex }; potential = potential.AddCycle(new DetachedCycle <int>(positiveCycleVertices), +1); potential = potential.AddCycle(new DetachedCycle <int>(negativeCycleVertices), -1); } } // Cobweb layers for (int layerIndex = 1; layerIndex < numLayers - 2; layerIndex++) // 0-based layer index { var curLayer = GetLayerVertices(layerIndex); var nextLayer = GetLayerVertices(layerIndex + 1); for (int indexInLayer = 0; indexInLayer < numVerticesInFullInnerLayer; indexInLayer++) { int sign = (layerIndex + indexInLayer).Modulo(2) == 0 ? +1 : -1; var squareVertices = sign == +1 ? new int[] { curLayer[indexInLayer + 1], curLayer[indexInLayer], nextLayer[indexInLayer], nextLayer[indexInLayer + 1], curLayer[indexInLayer + 1] } : new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[indexInLayer + 1], nextLayer[indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), sign); } } // Outermost layer of polygons { int layerIndex = numLayers - 2; var curLayer = GetLayerVertices(layerIndex); var nextLayer = GetLayerVertices(layerIndex + 1); for (int indexInPeriod = 0; indexInPeriod < numPeriods; indexInPeriod++) { // Add pentagon int indexInPenultimateLayer = 2 * indexInPeriod; int indexInUltimateLayer = 3 * indexInPeriod; int pentagonSign = layerIndex.Modulo(2) == 0 ? +1 : -1; // Remember that layerIndex is the index of the *penultimate* layer var pentagonVertices = pentagonSign == +1 ? new int[] { curLayer[indexInPenultimateLayer + 1], curLayer[indexInPenultimateLayer], nextLayer[indexInUltimateLayer], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer + 2], curLayer[indexInPenultimateLayer + 1] } : new int[] { curLayer[indexInPenultimateLayer], curLayer[indexInPenultimateLayer + 1], nextLayer[indexInUltimateLayer + 2], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer], curLayer[indexInPenultimateLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(pentagonVertices), pentagonSign); // Add square int squareSign = -pentagonSign; indexInPenultimateLayer += 1; indexInUltimateLayer += 2; var squareVertices = squareSign == +1 ? new int[] { curLayer[indexInPenultimateLayer + 1], curLayer[indexInPenultimateLayer], nextLayer[indexInUltimateLayer], nextLayer[indexInUltimateLayer + 1], curLayer[indexInPenultimateLayer + 1] } : new int[] { curLayer[indexInPenultimateLayer], curLayer[indexInPenultimateLayer + 1], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer], curLayer[indexInPenultimateLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), squareSign); } } var qp = new QuiverWithPotential <int>(potential); return(qp); CircularList <int> GetLayerVertices(int layerIndex) => new CircularList <int>(GetVerticesInPointedFlowerQPLayer(numPeriods, layerIndex, firstVertex)); }
public static QuiverWithPotential <int> GetEvenFlowerType2QP(int numVerticesInCenterPolygon, int firstVertex = DefaultFirstVertex) { if (!EvenFlowerType2ParameterIsValid(numVerticesInCenterPolygon)) { throw new ArgumentOutOfRangeException(nameof(numVerticesInCenterPolygon)); } int numLayers = UsefulQuivers.GetNumberOfLayersInEvenFlowerType2Quiver(numVerticesInCenterPolygon); int numVerticesInFullInnerLayer = 2 * numVerticesInCenterPolygon; int numVerticesInOuterLayer = 3 * numVerticesInCenterPolygon; var potential = new Potential <int>(); // Center polygon potential = potential.AddCycle(new DetachedCycle <int>(Enumerable.Range(firstVertex, numVerticesInCenterPolygon).AppendElement(firstVertex)), +1); // Full inner layers if (numLayers > 2) { // First layer (the squares and triangles between the first and second layers) var curLayer = GetLayerVertices(0); var nextLayer = GetLayerVertices(1); for (int indexInLayer = 0; indexInLayer < numVerticesInCenterPolygon; indexInLayer++) { var triangleVertices = new int[] { curLayer[indexInLayer], nextLayer[2 * indexInLayer - 1], nextLayer[2 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(triangleVertices), +1); var squareVertices = new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[2 * indexInLayer + 1], nextLayer[2 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), -1); } // Remaining layers (only squares) for (int layerIndex = 1; layerIndex < numLayers - 2; layerIndex++) // 0-based layer index { curLayer = GetLayerVertices(layerIndex); nextLayer = GetLayerVertices(layerIndex + 1); for (int indexInLayer = 0; indexInLayer < numVerticesInFullInnerLayer; indexInLayer++) { int sign = (layerIndex + indexInLayer).Modulo(2) == 1 ? +1 : -1; var squareVertices = sign == +1 ? new int[] { curLayer[indexInLayer + 1], curLayer[indexInLayer], nextLayer[indexInLayer], nextLayer[indexInLayer + 1], curLayer[indexInLayer + 1] } : new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[indexInLayer + 1], nextLayer[indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), sign); } } } // Outer layer { int layerIndex = numLayers - 2; var curLayer = GetLayerVertices(layerIndex); var nextLayer = GetLayerVertices(layerIndex + 1); if (numLayers == 2) { // Add squares and diamonds in this degenerate case for (int indexInLayer = 0; indexInLayer < curLayer.Count; indexInLayer++) { var diamondVertices = new int[] { curLayer[indexInLayer], nextLayer[3 * indexInLayer - 2], nextLayer[3 * indexInLayer - 1], nextLayer[3 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(diamondVertices), +1); var squareVertices = new int[] { curLayer[indexInLayer], curLayer[indexInLayer + 1], nextLayer[3 * indexInLayer + 1], nextLayer[3 * indexInLayer], curLayer[indexInLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), -1); } } else { // Add squares and pentagons in the non-degenerate case for (int indexInCenterLayer = 0; indexInCenterLayer < numVerticesInCenterPolygon; indexInCenterLayer++) { // Add square int indexInPenultimateLayer = 2 * indexInCenterLayer; int indexInUltimateLayer = 3 * indexInCenterLayer; int squareSign = layerIndex.Modulo(2) == 1 ? +1 : -1; // Remember that layerIndex is the index of the *penultimate* layer var squareVertices = squareSign == +1 ? new int[] { curLayer[indexInPenultimateLayer + 1], curLayer[indexInPenultimateLayer], nextLayer[indexInUltimateLayer], nextLayer[indexInUltimateLayer + 1], curLayer[indexInPenultimateLayer + 1] } : new int[] { curLayer[indexInPenultimateLayer], curLayer[indexInPenultimateLayer + 1], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer], curLayer[indexInPenultimateLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(squareVertices), squareSign); // Add pentagon int pentagonSign = -squareSign; indexInPenultimateLayer += 1; indexInUltimateLayer += 1; var pentagonVertices = pentagonSign == +1 ? new int[] { curLayer[indexInPenultimateLayer + 1], curLayer[indexInPenultimateLayer], nextLayer[indexInUltimateLayer], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer + 2], curLayer[indexInPenultimateLayer + 1] } : new int[] { curLayer[indexInPenultimateLayer], curLayer[indexInPenultimateLayer + 1], nextLayer[indexInUltimateLayer + 2], nextLayer[indexInUltimateLayer + 1], nextLayer[indexInUltimateLayer], curLayer[indexInPenultimateLayer] }; potential = potential.AddCycle(new DetachedCycle <int>(pentagonVertices), pentagonSign); } } } var qp = new QuiverWithPotential <int>(potential); return(qp); CircularList <int> GetLayerVertices(int layerIndex) => new CircularList <int>(GetVerticesInEvenFlowerType2QPLayer(numVerticesInCenterPolygon, layerIndex, firstVertex)); }
/// <summary> /// Creates a new instance of the <see cref="SemimonomialIdeal{TVertex}"/> class that /// represents the Jacobian ideal of the given potential (in some unspecified quiver /// containing all the arrows in the potential). /// </summary> /// <param name="potential">The potential whose semimonomial Jacobian ideal to create.</param> /// <returns>The semimonomial Jacobian ideal of <paramref name="potential"/>.</returns> /// <exception cref="NotSupportedException"><paramref name="potential"/> has a cycle with /// coefficient not equal to either of -1 and +1, /// or some arrow occurs multiple times in a single cycle of <paramref name="potential"/>.</exception> /// <exception cref="ArgumentException">For some arrow in <paramref name="potential"/> and /// sign, the arrow is contained in more than one cycle of that sign.</exception> /// <remarks> /// <para>The preconditions on <paramref name="potential"/> as of this writing is that the /// the scalars are <c>-1</c> or <c>+1</c>, every arrow occurs in at most one cycle per /// sign, and every arrow occurs at most once per cycle.</para> /// <para>The reasoning behind the differentiation between <see cref="NotSupportedException"/> /// and <see cref="ArgumentException"/> is that <see cref="NotSupportedException"/> is /// thrown when things might work out in theory (e.g., all scalars could be -2 or +2 /// instead of -1 or +1, or an arrow could appear more than once in a cycle if the cycle is /// a suitable power of some other cycle (and stars align if the arrow is also contained in /// a cycle of the opposite sign)), while <see cref="ArgumentException"/> is thrown when /// things do not work out even in theory (i.e., semimonomiality fails to hold). That is, /// <see cref="NotSupportedException"/> is thrown in cases that might be "fixed" /// in the future.</para> /// </remarks> public static SemimonomialIdeal <TVertex> CreateSemimonomialIdealFromPotential <TVertex>(Potential <TVertex> potential) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Validation var cycleCoefficients = new HashSet <int>(potential.LinearCombinationOfCycles.ElementToCoefficientDictionary.Values); if (!cycleCoefficients.IsSubsetOf(new int[] { -1, +1 })) { throw new NotSupportedException("Only potentials with all coefficients equal to +1 or -1 are supported."); } if (potential.Cycles.Any(cycle => cycle.Arrows.HasDuplicate())) { throw new NotSupportedException("Potentials with an arrow occurring more than once in a cycle are not supported."); } var signedCycles = potential.LinearCombinationOfCycles.ElementToCoefficientDictionary; var signedArrows = signedCycles.SelectMany(pair => { var cycle = pair.Key; var sign = pair.Value; return(cycle.Arrows.Select(arrow => (arrow, sign))); }); if (signedArrows.TryGetDuplicate(out var duplicate)) { throw new ArgumentException($"The potential has a signed arrow {duplicate} occurring in more than one cycle.", nameof(potential)); } var monomialGenerators = new List <Path <TVertex> >(); var nonMonomialGenerators = new List <DifferenceOfPaths <TVertex> >(); var arrows = potential.Cycles.SelectMany(cycle => cycle.Arrows).WithoutDuplicates(); foreach (var arrow in arrows) { // Guaranteed by the validation to have only terms with coefficients +1 or -1 // Also guaranteed to have at most 2 terms var linCombOfPaths = potential.DifferentiateCyclically(arrow); var numTerms = linCombOfPaths.NumberOfTerms; if (numTerms == 1) { monomialGenerators.Add(linCombOfPaths.Elements.Single()); } else if (numTerms == 2) { var paths = linCombOfPaths.Elements.ToList(); var differenceOfPaths = new DifferenceOfPaths <TVertex>(paths[0], paths[1]); nonMonomialGenerators.Add(differenceOfPaths); } else { System.Diagnostics.Debug.Fail($"{numTerms} terms in the cyclic derivative with respect to {arrow}. Expected at most two terms."); } } var semimonomialIdeal = new SemimonomialIdeal <TVertex>(monomialGenerators, nonMonomialGenerators); return(semimonomialIdeal); }