public bool Equals(QuiverWithPotential <TVertex> otherQP) { if (otherQP is null) { return(false); } return(Quiver.Equals(otherQP.Quiver) && Potential.Equals(otherQP.Potential)); }
/// <summary> /// Gets the "classic" weakly but not strongly cancellative QP. /// </summary> /// <returns>The "classic" weakly but not strongly cancellative QP.</returns> public static QuiverWithPotential <int> GetClassicWeaklyButNotStronglyCancellativeQP() { var baseQP = GetClassicNonCancellativeQP(); var vertices = baseQP.Quiver.Vertices.Append(6); var arrows = baseQP.Quiver.Arrows.Append(new Arrow <int>(4, 6)); var quiver = new Quiver <int>(vertices, arrows); var qp = new QuiverWithPotential <int>(quiver, baseQP.Potential); return(qp); }
/// <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 static QuiverWithPotential <int> GetCycleQP(int numVertices, int firstVertex = DefaultFirstVertex) { if (!CycleParameterIsValid(numVertices)) { throw new ArgumentOutOfRangeException(nameof(numVertices)); } int n = numVertices; var cycle = new DetachedCycle <int>(Enumerable.Range(firstVertex, n).Select(k => new Arrow <int>(k, (k + 1 - firstVertex).Modulo(n) + firstVertex))); var qp = new QuiverWithPotential <int>(new Potential <int>(cycle, +1)); return(qp); }
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); }
/// <summary> /// Initializes a new instance of the <see cref="SelfInjectiveQP{TVertex}"/> class. /// </summary> /// <param name="qp">The quiver with potential.</param> /// <param name="nakayamaPermutation">The Nakayama permutation.</param> /// <remarks>Almost no sanity checks are performed on the parameters.</remarks> public SelfInjectiveQP(QuiverWithPotential <TVertex> qp, IReadOnlyDictionary <TVertex, TVertex> nakayamaPermutation) : this(qp, new NakayamaPermutation <TVertex>(nakayamaPermutation)) { }
/// <summary> /// Initializes a new instance of the <see cref="SelfInjectiveQP{TVertex}"/> class. /// </summary> /// <param name="qp">The quiver with potential.</param> /// <param name="nakayamaPermutation">The Nakayama permutation.</param> /// <remarks>Almost no sanity checks are performed on the parameters.</remarks> public SelfInjectiveQP(QuiverWithPotential <TVertex> qp, NakayamaPermutation <TVertex> nakayamaPermutation) { QP = qp ?? throw new ArgumentNullException(nameof(qp)); NakayamaPermutation = nakayamaPermutation ?? throw new ArgumentNullException(nameof(nakayamaPermutation)); }
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)); }
// TODO: Output the isomorphism as well! public bool AreIsomorphic <TVertex1, TVertex2>(QuiverWithPotential <TVertex1> qp1, QuiverWithPotential <TVertex2> qp2) where TVertex1 : IEquatable <TVertex1>, IComparable <TVertex1> where TVertex2 : IEquatable <TVertex2>, IComparable <TVertex2> { if (qp1 == null) { throw new ArgumentNullException(nameof(qp1)); } if (qp2 == null) { throw new ArgumentNullException(nameof(qp2)); } if (qp1.Quiver.Vertices.Count != qp2.Quiver.Vertices.Count) { return(false); } var positiveCycleGroups1 = GetCyclesGroupedByLength(qp1, +1); var negativeCycleGroups1 = GetCyclesGroupedByLength(qp1, -1); var positiveCycleGroups2 = GetCyclesGroupedByLength(qp2, +1); var negativeCycleGroups2 = GetCyclesGroupedByLength(qp2, -1); // Check that the number of cycles of a fixed length and sign in qp1 and qp2 are equal, for every length and sign var posTuples1 = positiveCycleGroups1.Select(group => (group.First().Length, group.Count())); var posTuples2 = positiveCycleGroups2.Select(group => (group.First().Length, group.Count())); if (!posTuples1.EqualUpToOrder(posTuples2)) { return(false); } var negTuples1 = negativeCycleGroups1.Select(group => (group.First().Length, group.Count())); var negTuples2 = negativeCycleGroups2.Select(group => (group.First().Length, group.Count())); if (!negTuples1.EqualUpToOrder(negTuples2)) { return(false); } GetVertexMaps(out var vertexMap1, out var vertexMap2, out var abstractVertices); var z3 = new Context(); var goal = z3.MkGoal(models: true, unsatCores: true, proofs: false); string[] vertexNames = abstractVertices.Select(k => $"V{k}").ToArray(); var vertexSort = z3.MkEnumSort("Vertex", vertexNames); var z3Vertices = vertexSort.Consts; var vertexProduct = from v1 in abstractVertices from v2 in abstractVertices select(v1, v2); // Define uninterpreted adjacency functions and provide assertions for their every value. // This is the only way to "define" a function in the API it seems. // The alternative, to muddy up the assertions on f, does not seem appetizing. var a1 = z3.MkFuncDecl("a1", new Sort[] { vertexSort, vertexSort }, z3.MkBoolSort()); foreach (var(v1, v2) in vertexProduct) { var z3Vertex1 = z3Vertices[v1]; var z3Vertex2 = z3Vertices[v2]; bool arrowInQp1 = qp1.Quiver.AdjacencyLists[vertexMap1[v1]].Contains(vertexMap1[v2]); goal.Assert(z3.MkEq(a1.Apply(z3Vertex1, z3Vertex2), z3.MkBool(arrowInQp1))); } var a2 = z3.MkFuncDecl("a2", new Sort[] { vertexSort, vertexSort }, z3.MkBoolSort()); foreach (var(v1, v2) in vertexProduct) { var z3Vertex1 = z3Vertices[v1]; var z3Vertex2 = z3Vertices[v2]; bool arrowInQp2 = qp2.Quiver.AdjacencyLists[vertexMap2[v1]].Contains(vertexMap2[v2]); goal.Assert(z3.MkEq(a2.Apply(z3Vertex1, z3Vertex2), z3.MkBool(arrowInQp2))); } var f = z3.MkFuncDecl("f", vertexSort, vertexSort); goal.Assert(z3.MkDistinct(vertexSort.Consts.Select(v => f.Apply(v)).ToArray())); foreach (var(v1, v2) in vertexProduct) { var z3Vertex1 = z3Vertices[v1]; var z3Vertex2 = z3Vertices[v2]; bool arrowInQp1 = qp1.Quiver.AdjacencyLists[vertexMap1[v1]].Contains(vertexMap1[v2]); goal.Assert(z3.MkEq(a1.Apply(z3Vertex1, z3Vertex2), a2.Apply(f.Apply(z3Vertex1), f.Apply(z3Vertex2)))); } // Define uninterpreted constants for the cycle vertices // Store the constants in a dictionary indexed by cycle length and values a nested list indexed by cycleIndex (for cycles of that length and sign) and vertexIndex (in cycle) var positiveCycleLengthToCycleConsts1 = GetCycleConsts(1, +1, positiveCycleGroups1); var negativeCycleLengthToCycleConsts1 = GetCycleConsts(1, -1, negativeCycleGroups1); var positiveCycleLengthToCycleConsts2 = GetCycleConsts(2, +1, positiveCycleGroups2); var negativeCycleLengthToCycleConsts2 = GetCycleConsts(2, -1, negativeCycleGroups2); AssertThatFMapsEveryCycleToAnotherCycleOfTheSameSign(positiveCycleLengthToCycleConsts1, positiveCycleLengthToCycleConsts2); AssertThatFMapsEveryCycleToAnotherCycleOfTheSameSign(negativeCycleLengthToCycleConsts1, negativeCycleLengthToCycleConsts2); var solver = z3.MkSolver(); var status = solver.Check(); switch (status) { case Status.SATISFIABLE: return(true); case Status.UNSATISFIABLE: return(false); case Status.UNKNOWN: throw new NotImplementedException(); default: throw new NotImplementedException(); } void AssertThatFMapsEveryCycleToAnotherCycleOfTheSameSign( Dictionary <int, List <CircularList <Expr> > > cycleLengthToCycleConsts1, Dictionary <int, List <CircularList <Expr> > > cycleLengthToCycleConsts2) { foreach (var cycleLength in cycleLengthToCycleConsts1.Keys) { var cycles1 = cycleLengthToCycleConsts1[cycleLength]; var cycles2 = cycleLengthToCycleConsts2[cycleLength]; foreach (var cycle in cycles1) { var expr = GetBooleanExpressionForCycleBeingMappedToOneOfManyCycles(cycle, cycles2); goal.Assert(expr); } } } BoolExpr GetBooleanExpressionForCycleBeingMappedToOneOfManyCycles( CircularList <Expr> cycle1, IEnumerable <CircularList <Expr> > cycles) { return(z3.MkOr(cycles.Select(cycle2 => GetBooleanExpressionForCycleBeingMappedToCycle(cycle1, cycle2)))); } BoolExpr GetBooleanExpressionForCycleBeingMappedToCycle( CircularList <Expr> cycle1, CircularList <Expr> cycle2) { return(z3.MkOr(Enumerable.Range(0, cycle1.Count).Select(_ => { cycle2.RotateLeft(1); return GetBooleanExpressionForPathBeingMappedToPath(cycle1, cycle2); }))); } BoolExpr GetBooleanExpressionForPathBeingMappedToPath( IEnumerable <Expr> path1, IEnumerable <Expr> path2) { return(z3.MkAnd(path1.Zip(path2, (v1, v2) => z3.MkEq(f.Apply(v1), v2)))); } Dictionary <int, List <CircularList <Expr> > > GetCycleConsts <TVertex>( int qpNum, int sign, IEnumerable <IGrouping <int, DetachedCycle <TVertex> > > cycleGroups) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { string signString = sign > 0 ? "P" : "N"; var output = new Dictionary <int, List <CircularList <Expr> > >(); foreach (var cycleGroup in cycleGroups) { var cycleGroupLength = cycleGroup.First().Length; output[cycleGroupLength] = new List <CircularList <Expr> >(); foreach (var(cycle, cycleIndex) in cycleGroup.EnumerateWithIndex()) { output[cycleGroupLength].Add(new CircularList <Expr>()); foreach (var(vertex, vertexIndex) in cycle.CanonicalPath.Vertices.EnumerateWithIndex()) { var constant = z3.MkConst($"q{qpNum}{signString}L{cycleGroupLength}C{cycleIndex}V{vertexIndex}", vertexSort); output[cycleGroupLength][cycleIndex].Add(constant); } } } return(output); } IEnumerable <IGrouping <int, DetachedCycle <TVertex> > > GetCyclesGroupedByLength <TVertex>(QuiverWithPotential <TVertex> qp, int sign) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { // Can't deconstruct the argument as (pair, coeff), because it matches the (pair, index) overload of Where and Select return(qp.Potential.LinearCombinationOfCycles.ElementToCoefficientDictionary.Where(pair => pair.Value == sign) .Select(pair => pair.Key) .GroupBy(c => c.Length)); } // Would be better to do this with only one QP and call the function twice imo void GetVertexMaps(out Dictionary <int, TVertex1> _vertexMap1, out Dictionary <int, TVertex2> _vertexMap2, out int[] _abstractVertices) { _vertexMap1 = new Dictionary <int, TVertex1>(); int vertexNumber = 0; foreach (var vertex in qp1.Quiver.Vertices) { _vertexMap1[vertexNumber++] = vertex; } _vertexMap2 = new Dictionary <int, TVertex2>(); vertexNumber = 0; foreach (var vertex in qp2.Quiver.Vertices) { _vertexMap2[vertexNumber++] = vertex; } int _vertexCount = vertexNumber; _abstractVertices = Enumerable.Range(0, _vertexCount).ToArray(); } }
/// <summary> /// Creates a new instance of the <see cref="SemimonomialUnboundQuiver{TVertex}"/> class /// whose ideal is the Jacobian ideal of the potential of the given QP. /// </summary> /// <param name="qp">The QP whose corresponding semimonomial unbound quiver to create.</param> /// <returns>The semimonomial unbound quiver induced by <paramref name="qp"/>.</returns> /// <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> /// <remarks> /// <para>The preconditions on the potential of <paramref name="qp"/> 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 SemimonomialUnboundQuiver <TVertex> CreateSemimonomialUnboundQuiverFromQP <TVertex>(QuiverWithPotential <TVertex> qp) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { var semimonomialIdeal = SemimonomialIdealFactory.CreateSemimonomialIdealFromPotential(qp.Potential); var semimonomialUnboundQuiver = new SemimonomialUnboundQuiver <TVertex>(qp.Quiver, semimonomialIdeal); return(semimonomialUnboundQuiver); }