public static Potential <int> GetUnnamedFamily2Potential(int n, int m) { if (n <= 1) { throw new ArgumentOutOfRangeException(nameof(n)); } if (m <= 2) { throw new ArgumentOutOfRangeException(nameof(m)); // Not sure about smaller m; they might work } var ngonArrows = Enumerable.Range(0, n).Select(k => n - k).Select(k => new Arrow <int>(k % n, k - 1)); var ngonCycle = new DetachedCycle <int>(new Path <int>(ngonArrows)); var cycleDict = new Dictionary <DetachedCycle <int>, int>() { { ngonCycle, -1 } }; foreach (var k in Enumerable.Range(0, n)) { var mgonArrows = Enumerable.Range(0, m - 2).Select(j => new Arrow <int>(k + j * n, k + (j + 1) * n)) .AppendElement(new Arrow <int>(k + (m - 2) * n, (k + 1) % n)) .AppendElement(new Arrow <int>((k + 1) % n, k)); var mgonCycle = new DetachedCycle <int>(new Path <int>(mgonArrows)); cycleDict[mgonCycle] = 1; } return(new Potential <int>(cycleDict)); }
public void NeqOperator_OnNull() { DetachedCycle <int> nullCycle = null; var cycle = CreateCycle(1, new Arrow <int>(1, 2), new Arrow <int>(2, 3), new Arrow <int>(3, 1)); Assert.That(nullCycle != null, Is.False); Assert.That(cycle != null, Is.True); }
public static Potential <int> GetCyclePotential(int n) { if (n <= 1) { throw new ArgumentOutOfRangeException(nameof(n)); } var arrows = Enumerable.Range(1, n - 1).Select(k => new Arrow <int>(k, k + 1)).AppendElement(new Arrow <int>(n, 1)); var cycle = new DetachedCycle <int>(new Path <int>(1, arrows)); var cycleDict = new Dictionary <DetachedCycle <int>, int>() { { cycle, 1 }, }; return(new Potential <int>(cycleDict)); }
public static Potential <int> GetUnnamedFamily1Potential(int n) { if (n <= 1) { throw new ArgumentOutOfRangeException(nameof(n)); } var ngonArrows = Enumerable.Range(0, n).Select(k => n - k).Select(k => new Arrow <int>(k % n, k - 1)); var ngonCycle = new DetachedCycle <int>(new Path <int>(ngonArrows)); var cycleDict = new Dictionary <DetachedCycle <int>, int>() { { ngonCycle, -1 } }; foreach (var k in Enumerable.Range(0, n)) { var triangleArrows = new Arrow <int>[] { new Arrow <int>(k, k + n), new Arrow <int>(k + n, (k + 1) % n), new Arrow <int>((k + 1) % n, k) }; var triangleCycle = new DetachedCycle <int>(new Path <int>(triangleArrows)); cycleDict[triangleCycle] = 1; } return(new Potential <int>(cycleDict)); }
static Potential <int> CreatePotential(DetachedCycle <int> cycle, int coefficient) { return(new Potential <int>(cycle, coefficient)); }
private IEnumerable <IEnumerable <TVertex> > SearchForFaces <TVertex>(IReadOnlyUndirectedGraph <TVertex> graph, Orientation searchOrientation) where TVertex : IEquatable <TVertex>, IComparable <TVertex>, IVertexInPlane { // Algorithm (outline): // Keep track of traversed edges *and the direction in which the edge was traversed* // (i.e., keep track of traversed directed edges). // // For every directed edge (not already traversed), make a search: // Keep track of the path of edges traversed. // Start by traversing that edge. // Keep turning "maximally" in the direction given by the search orientation until a vertex is visited twice. // This gives a full path that decomposes into a path and a cycle. // The path always consists of only boundary directed edges (boundary w.r.t. the search orientation), // while the cycle bounds a (bounded) face precisely when its orientation agrees with the search orientation. // Remark: Sort of bad to use Arrow here (writing a DirectedEdge class with a common interface would be a proper solution), // but it allows so much code to be reused. var cycles = new HashSet <DetachedCycle <TVertex> >(); var directedEdges = graph.Edges.SelectMany(e => new Arrow <TVertex>[] { new Arrow <TVertex>(e.Vertex1, e.Vertex2), new Arrow <TVertex>(e.Vertex2, e.Vertex1) }); var remainingEdges = new HashSet <Arrow <TVertex> >(directedEdges); var remainingEdgesStack = new Stack <Arrow <TVertex> >(directedEdges); while (remainingEdges.Count > 0) { Arrow <TVertex> startEdge; while (!remainingEdges.Contains(startEdge = remainingEdgesStack.Pop())) { ; } var path = new Path <TVertex>(startEdge.Source, startEdge.Target); var prevVertex = startEdge.Source; var curVertex = startEdge.Target; // Begin the search for this start edge! while (true) { // Terminate the search if we have found a cycle if (path.TryExtractTrailingCycle(out var closedPath, out int startIndex)) { var cycleOrientation = graph.GetOrientationOfCycle(closedPath.Vertices); // 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 (directed edge), we might get the same cycle again if (!cycles.Contains(detachedCycle)) { cycles.Add(new DetachedCycle <TVertex>(closedPath)); } } foreach (var edge in path.Arrows) { remainingEdges.Remove(edge); } 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 neighbors = graph.AdjacencyLists[curVertex]; // Note that this includes prevVertex if (neighbors.Count == 1) // The last edge was a dead end! { foreach (var arrow in path.Arrows) { remainingEdges.Remove(arrow); } break; } var baseVertex = curVertex; var basePos = baseVertex.Position; // Sort the vertices by angle so that the vertex following the predecessor vertex (prevVertex) is // the vertex corresponding to a "maximal" turn. var neighborsSortedByAngle = searchOrientation == Orientation.Clockwise ? neighbors.OrderBy(vertex => vertex.Position, new AngleBasedPointComparer(basePos)) : neighbors.OrderByDescending(vertex => vertex.Position, new AngleBasedPointComparer(basePos)); var neighborsSortedByAngleList = new CircularList <TVertex>(neighborsSortedByAngle); int predecessorIndex = neighborsSortedByAngleList.IndexOf(prevVertex); neighborsSortedByAngleList.RotateLeft(predecessorIndex); var nextVertex = neighborsSortedByAngleList.Skip(1).First(); path = path.AppendVertex(nextVertex); prevVertex = curVertex; curVertex = nextVertex; } } return(cycles.Select(cycle => cycle.CanonicalPath.Vertices)); }