private List <NodeConfig> DetermineMavens() { var mavens = new List <NodeConfig>(); var graph = new UndirectedGraph <string, Edge <string> >(); foreach (var config in _configs) { graph.AddVertex(config.CurrentNode.Name); foreach (var connection in config.Connections) { graph.AddVertex(connection.Name); graph.AddEdge(new Edge <string>(config.CurrentNode.Name, connection.Name)); } } var components = new Dictionary <string, int>(); graph.ConnectedComponents(components); foreach (var i in components.Values.Distinct()) { var names = components.Where(p => p.Value == i).Select(p => p.Key); var node = _configs.Where(config => names.Contains(config.CurrentNode.Name)) .OrderByDescending(x => x.Connections.Count).First(); mavens.Add(node); } return(mavens); }
public int GetConnectedComponents() { var graph = new UndirectedGraph <Voxel, Edge <Voxel> >(); graph.AddVertexRange(GetVoxels().Where(v => v.IsActive)); graph.AddEdgeRange(GetFaces().Where(f => f.IsActive).Select(f => new Edge <Voxel>(f.Voxels[0], f.Voxels[1]))); var components = new Dictionary <Voxel, int>(); var count = graph.ConnectedComponents(components); return(count); }
private void Process() { //Break tree on all edges and label as unlocked (contains start vertex) and locked tree foreach (var edgeToSplitOn in edgesToSplitOn) { map.RemoveEdge(edgeToSplitOn); } //We need to get the 2 subtrees created by this divide //This could be done by DFS from the source and target of the edge //Unfortunately, we don't have a top-level method to do that, so we get the connected components instead (may be slower) int componentCount = map.ConnectedComponents <int, TaggedEdge <int, string> >(components); //Which tree is the unlocked one? OriginComponentIndex = components[originVertex]; }
public MapCycleReducer(IEnumerable <TaggedEdge <int, string> > edges) { this.baseGraph = new UndirectedGraph <int, TaggedEdge <int, string> >(); baseGraph.AddVerticesAndEdgeRange(edges); //Find minimum spanning tree mapMST = new MapMST(baseGraph.Edges); //Find all cycles within tree if (CycleDebug) { Console.WriteLine("--cycle finding--"); } //Find all edges not in MST var allEdges = baseGraph.Edges; var edgesInMST = mapMST.vertexPredecessors.Values; var backEdges = allEdges.Except(edgesInMST); if (CycleDebug) { Console.WriteLine("No of back edges: {0}", backEdges.Count()); foreach (var edge in backEdges) { Console.WriteLine(edge); } } //Calculate [all? - expensive?] shortest paths. Each edge has a distance of 1 var cycleList = new List <IEnumerable <TaggedEdge <int, string> > >(); //Find the shortest cycle for each back edge foreach (var backEdge in backEdges) { int startVertex = backEdge.Source; int endVertex = backEdge.Target; var tryGetPath = mapMST.mst.ShortestPathsDijkstra(x => 1, startVertex); IEnumerable <TaggedEdge <int, string> > path; if (tryGetPath(endVertex, out path)) { cycleList.Add(path); AddToAllCycles(path, backEdge); } else { Console.WriteLine(String.Format("no path found for cycle, start: {0}, end: {1}", startVertex, endVertex)); } } //Output to console all cycles if (CycleDebug) { foreach (var cycle in cycleList) { Console.WriteLine("Cycle: "); foreach (var edge in cycle) { Console.Write(String.Format("{0}\t", edge)); } Console.Write("\r\n"); } } //Combine any cycles that share a vertex //There will be at least 2 ways of getting to each vertex //Therefore these vertexes must be collasped //make a (probably unconnected) graph of all the vertexes in the cycles //(no need to add back-edges - if they are connected by this then they will be connected by 2 vertices) var cycleGraph = new UndirectedGraph <int, TaggedEdge <int, string> >(); var allCycleEdges = cycleList.SelectMany(lst => lst); cycleGraph.AddVerticesAndEdgeRange(allCycleEdges); //find the connected components //this gives us n sets of connected nodes which will be reduced to n single nodes in the final acyclic graph var components = new Dictionary <int, int>(); int componentCount = cycleGraph.ConnectedComponents <int, TaggedEdge <int, string> >(components); if (componentCount != 0) { if (CycleDebug) { Console.WriteLine("Graph contains {0} strongly connected components", componentCount); } foreach (var kv in components) { if (CycleDebug) { Console.WriteLine("Vertex {0} is connected to subgraph {1}", kv.Key, kv.Value); } } } //Replace each connected component with a single vertex, and remember which nodes were rolled-up mapNoCycles.AddVerticesAndEdgeRange(baseGraph.Edges); //Maintain a map of vertex number mappings after cycle removal //Initialise with no-change case var roomMappingToNoCyclesWork = new Dictionary <int, int>(); foreach (var vertex in baseGraph.Vertices) { roomMappingToNoCyclesWork[vertex] = vertex; } //Maintain a map of all edges after cycle removal to initial map var edgeMappingNoCycleToFullMapWork = new Dictionary <Connection, Connection>(); //For each cycle for (int i = 0; i < componentCount; i++) { //Get all vertices in this cycle //Get all vertices (keys) with value (cycle no) var verticesInCycle = components.Where(kv => kv.Value == i).Select(kv => kv.Key); //First vertex (to be kept) //Other vertices (to be removed) var sortedVertices = verticesInCycle.ToList(); sortedVertices.Sort(); //First vertex defined as minimum to ease repeatibility var firstVertex = sortedVertices.First(); var verticesInCycleNotFirst = sortedVertices.Skip(1); //Get all non-internal edges from vertices in the cycle //Get all adjacent edges to vertices to remove in the cycle var edgesFromCycle = verticesInCycleNotFirst.SelectMany(v => baseGraph.AdjacentEdges(v)); //Discard all edges between vertices //(we need to maintain edges that are either sourced from the cycle or target it) var exteriorEdges = edgesFromCycle.Where(edge => !verticesInCycle.Contains(edge.Source) || !verticesInCycle.Contains(edge.Target)); //Remove all cycle vertices but first from graph foreach (int vertex in verticesInCycleNotFirst) { //Update vertex map roomMappingToNoCyclesWork[vertex] = firstVertex; //Remove vertex from graph mapNoCycles.RemoveVertex(vertex); } //Add all exterior edges onto the remaining cycle vertex foreach (var edge in exteriorEdges) { //Maintain reverse mapping. Edges which are collasped map back to the original rooms (where the door actually is) //Store in lowest node first ordering edgeMappingNoCycleToFullMapWork.Add(new Connection(roomMappingToNoCyclesWork[edge.Source], roomMappingToNoCyclesWork[edge.Target]).Ordered, new Connection(edge.Source, edge.Target).Ordered); //Rewrite edge //Use mapped vertex indices, since those in this cycle (and other source cycles) will have been reduced mapNoCycles.AddEdge(new TaggedEdge <int, string>(roomMappingToNoCyclesWork[edge.Source], roomMappingToNoCyclesWork[edge.Target], edge.Tag)); } } //Fill in any unchanged edges in the edge mapping foreach (var edge in mapNoCycles.Edges) { if (!edgeMappingNoCycleToFullMapWork.ContainsKey(new Connection(edge.Source, edge.Target).Ordered)) { edgeMappingNoCycleToFullMapWork[new Connection(edge.Source, edge.Target).Ordered] = new Connection(edge.Source, edge.Target).Ordered; } } edgeMappingNoCycleToFullMap = edgeMappingNoCycleToFullMapWork.AsReadOnly(); roomMappingFullToNoCycleMap = roomMappingToNoCyclesWork.AsReadOnly(); //Reverse the above mapping var roomMappingFullToNoCycleGrouped = roomMappingToNoCyclesWork.GroupBy(kv => kv.Value); roomMappingNoCycleToFullMap = roomMappingFullToNoCycleGrouped.ToDictionary(g => g.Key, g => g.Select(kv => kv.Key).ToList()).AsReadOnly(); if (CycleDebug) { Console.WriteLine(String.Format("Cycle reduction - Cycles removed: {2}, Vertices before: {0}, vertices after: {1}", baseGraph.Vertices.Count(), mapNoCycles.Vertices.Count(), componentCount)); } }