public void Clone() { var graph = new UndirectedGraph <int, Edge <int> >(); AssertEmptyGraph(graph); var clonedGraph = graph.Clone(); Assert.IsNotNull(clonedGraph); AssertEmptyGraph(clonedGraph); clonedGraph = (UndirectedGraph <int, Edge <int> >)((ICloneable)graph).Clone(); Assert.IsNotNull(clonedGraph); AssertEmptyGraph(clonedGraph); var edge1 = new Edge <int>(1, 2); var edge2 = new Edge <int>(1, 3); var edge3 = new Edge <int>(2, 3); graph.AddVerticesAndEdgeRange(new[] { edge1, edge2, edge3 }); AssertHasVertices(graph, new[] { 1, 2, 3 }); AssertHasEdges(graph, new[] { edge1, edge2, edge3 }); clonedGraph = graph.Clone(); Assert.IsNotNull(clonedGraph); AssertHasVertices(clonedGraph, new[] { 1, 2, 3 }); AssertHasEdges(clonedGraph, new[] { edge1, edge2, edge3 }); clonedGraph = (UndirectedGraph <int, Edge <int> >)((ICloneable)graph).Clone(); Assert.IsNotNull(clonedGraph); AssertHasVertices(clonedGraph, new[] { 1, 2, 3 }); AssertHasEdges(clonedGraph, new[] { edge1, edge2, edge3 }); }
public void CloneTest() { var graph = new UndirectedGraph(); var newVertex1 = new Vertex("test-1"); var newVertex2 = new Vertex("test-2"); var newVertex3 = new Vertex("test-3"); graph.AddVertex(newVertex1); graph.AddVertex(newVertex2); graph.AddVertex(newVertex3); var newEdge1 = new UndirectedEdge(newVertex1, newVertex2); var newEdge2 = new UndirectedEdge(newVertex1, newVertex3); graph.AddEdge(newEdge1); graph.AddEdge(newEdge2); var clonedGraph = graph.Clone() as IGraph; Assert.IsTrue(clonedGraph is UndirectedGraph); Assert.AreEqual(graph.VerticesCount, clonedGraph.VerticesCount); foreach (var vertex in graph.Vertices) { var clonedVertex = clonedGraph.Vertices.Single(v => v.Equals(vertex)); Assert.AreNotSame(clonedVertex, vertex); } Assert.AreEqual(graph.EdgesCount, clonedGraph.EdgesCount); foreach (var clonedEdge in clonedGraph.Edges) { Assert.IsTrue(clonedEdge is UndirectedEdge); var edge = graph.Edges.Single(e => e.Equals(clonedEdge)); Assert.AreNotSame(edge, clonedEdge); } }
Triangulate(UndirectedGraph <int, IEdge <int> > tree, int root = 0) { Dictionary <(int source, int target), int> innerVerticesCount = new Dictionary <(int source, int target), int>(); UndirectedGraph <int, IEdge <int> > res = new UndirectedGraph <int, IEdge <int> >(); if (tree.IsVerticesEmpty) { return(res, innerVerticesCount); } if (!tree.ContainsVertex(root)) { throw new ArgumentException("root"); } // create counter-clockwise combinatorial embedding // assumes vertices are numbered as in BFS // so it's proper to add them to list in order they are stored internally UndirectedGraph <int, IEdge <int> > G = tree.Clone(); Dictionary <int, List <int> > embedding = new Dictionary <int, List <int> >(); // TODO: what if graph vertices are only subgraph? this assumption may be wrong foreach (int i in G.Vertices) { embedding.Add(i, new List <int>()); foreach (int v in G.AdjacentVertices(i)) { embedding[i].Add(v); } } foreach (int v in tree.Vertices) { int[] neighbors = new int[embedding[v].Count]; embedding[v].CopyTo(neighbors); int prev = neighbors[0]; for (int i = 1; i < neighbors.Length; i++) { int curr = neighbors[i]; TryAddEdge(prev, curr, v, ref G, ref res, ref embedding, ref innerVerticesCount); prev = curr; } if (neighbors.Length > 2) { TryAddEdge(prev, neighbors[0], v, ref G, ref res, ref embedding, ref innerVerticesCount); } } return(res, innerVerticesCount); }
public GraphColor[] Find3Colorings(UndirectedGraph <int, IEdge <int> > graph) { //Initialize structures _graph = graph.Clone(); _availableColors = new HashSet <GraphColor> [_graph.VertexCount]; for (int i = 0; i < _graph.VertexCount; i++) { _availableColors[i] = new HashSet <GraphColor>() { GraphColor.Black, GraphColor.Gray, GraphColor.White } } ; _coloring = new GraphColor?[_graph.VertexCount]; //Get all components of G List <UndirectedGraph <int, IEdge <int> > > G_components = FindComponents(_graph); foreach (UndirectedGraph <int, IEdge <int> > component in G_components) { //Find separator S for component HashSet <int> S = PlanarSeparator.FindSeparator(component); ////Split component into smaller components foreach (int v in S) { component.RemoveVertex(v); } List <UndirectedGraph <int, IEdge <int> > > components = FindComponents(component); //One of components cannot be colored => whole G cannot be colored //Slow version if (!DnCColoring(components, S)) { return(null); } //Fast version //if (!BruteForceColoring(new List<UndirectedGraph<int, IEdge<int>>>(), _graph.Vertices.ToHashSet())) // return null; } return(_coloring.Select(c => c.Value).ToArray()); }
public GraphColor[] Find3Colorings(UndirectedGraph <int, IEdge <int> > graph) { //Initialize structures _graph = graph.Clone(); _availableColors = new HashSet <GraphColor> [_graph.VertexCount]; for (int i = 0; i < _graph.VertexCount; i++) { _availableColors[i] = new HashSet <GraphColor>() { GraphColor.Black, GraphColor.Gray, GraphColor.White } } ; _coloring = new GraphColor?[_graph.VertexCount]; //Get all components of G (List <UndirectedGraph <int, IEdge <int> > > list, Dictionary <int, int> dict)G_components = FindComponents(_graph); foreach (UndirectedGraph <int, IEdge <int> > component in G_components.list) { //Find separator S for component HashSet <int> S = PlanarSeparator.FindSeparator(component); ////Split component into smaller components foreach (int v in S) { component.RemoveVertex(v); } (List <UndirectedGraph <int, IEdge <int> > >, Dictionary <int, int>)components = FindComponents(component); //One of components cannot be colored => whole G cannot be colored if (!DnCColoring(components, S).isColorable) { return(null); } } return(_coloring.Select(c => c.Value).ToArray()); }
Triangulate(UndirectedGraph <int, IEdge <int> > tree, int root = 0) { UndirectedGraph <int, IEdge <int> > res = new UndirectedGraph <int, IEdge <int> >(); Dictionary <(int source, int target), int> innerVerticesCount = new Dictionary <(int source, int target), int>(); if (tree.IsVerticesEmpty) { return(res, innerVerticesCount); } if (!tree.ContainsVertex(root)) { throw new ArgumentException("root"); } UndirectedGraph <int, IEdge <int> > G = tree.Clone(); // create counter-clockwise combinatorial embedding // assumes vertices are numbered as in BFS // so it's proper to add them to list in order they are stored internally Dictionary <int, List <int> > embedding = new Dictionary <int, List <int> >(); for (int i = 0; i < G.VertexCount; i++) { embedding.Add(i, new List <int>()); foreach (int v in G.AdjacentVertices(i)) { embedding[i].Add(v); } } // BFS Queue <int> q = new Queue <int>(); bool[] enqueued = new bool[G.VertexCount]; q.Enqueue(root); enqueued[root] = true; while (q.Count > 0) { int p = q.Dequeue(); int[] neighbors = new int[embedding[p].Count]; embedding[p].CopyTo(neighbors); int first = neighbors[0]; if (!enqueued[first]) { q.Enqueue(first); enqueued[first] = true; } int prev = first; int curr = -1; for (int i = 1; i < neighbors.Length; i++) { curr = neighbors[i]; TryAddEdge(prev, curr, p, ref G, ref res, ref embedding, ref innerVerticesCount); prev = curr; if (!enqueued[curr]) { q.Enqueue(curr); enqueued[curr] = true; } } if (neighbors.Length > 2) { TryAddEdge(curr, first, p, ref G, ref res, ref embedding, ref innerVerticesCount); } } return(res, innerVerticesCount); }
/// <summary> /// Algo 1: Find subgraph frequency (mappings found are saved to disk to be retrieved later during Algo 3). /// The value of the dictionary returned is in the form: $"{mappings.Count}#{qGraph.Label}.ser" /// </summary> /// <param name="inputGraph"></param> /// <param name="qGraph">The query graph to be searched for. If not available, we use expansion trees (MODA). Otherwise, we use Grochow's (Algo 2)</param> /// <param name="subgraphSize"></param> /// <param name="thresholdValue">Frequency value, above which we can comsider the subgraph a "frequent subgraph"</param> /// <returns></returns> public static Dictionary <QueryGraph, string> Algorithm1_C(UndirectedGraph <int> inputGraph, QueryGraph qGraph, int subgraphSize, int thresholdValue) { // The enumeration module (Algo 3) needs the mappings generated from the previous run(s) Dictionary <QueryGraph, string> allMappings; int numIterations = -1; if (inputGraph.VertexCount < 121) { numIterations = inputGraph.VertexCount; } if (qGraph == null) // Use MODA's expansion tree { #region Use MODA's expansion tree var treatedNodes = new HashSet <QueryGraph>(); allMappings = new Dictionary <QueryGraph, string>(_builder.NumberOfQueryGraphs); do { qGraph = GetNextNode()?.QueryGraph; if (qGraph == null) { break; } ICollection <Mapping> mappings; if (qGraph.EdgeCount == (subgraphSize - 1)) // i.e. if qGraph is a tree { if (UseModifiedGrochow) { // Modified Mapping module - MODA and Grockow & Kellis mappings = Algorithm2_Modified(qGraph, inputGraph, numIterations, false); } else { var inputGraphClone = inputGraph.Clone(); mappings = Algorithm2(qGraph, inputGraphClone, numIterations, false); inputGraphClone.Clear(); inputGraphClone = null; } // Because we're saving to file, we're better off doing this now qGraph.RemoveNonApplicableMappings(mappings, inputGraph, false); treatedNodes.Add(qGraph); } else { // Enumeration moodule - MODA // This is part of Algo 3; but performance tweaks makes it more useful to get it here var parentQueryGraph = GetParent(qGraph, _builder.ExpansionTree); if (parentQueryGraph.EdgeCount == (subgraphSize - 1)) { treatedNodes.Add(parentQueryGraph); } string _filename; if (allMappings.TryGetValue(parentQueryGraph, out _filename)) { string newFileName; // for parentQueryGraph mappings = Algorithm3(null, inputGraph, qGraph, _builder.ExpansionTree, parentQueryGraph, out newFileName, _filename); if (!string.IsNullOrWhiteSpace(newFileName)) { // We change the _filename value in the dictionary since this means some of the mappings from parent fit the child allMappings[parentQueryGraph] = newFileName; } } else { mappings = new Mapping[0]; } } if (mappings.Count > thresholdValue) { qGraph.IsFrequentSubgraph = true; } // Save mappings. var fileName = qGraph.WriteMappingsToFile(mappings); if (mappings.Count > 0) { mappings.Clear(); } allMappings.Add(qGraph, fileName); // Check for complete-ness; if complete, break if (qGraph.IsComplete(subgraphSize)) { qGraph = null; break; } qGraph = null; }while (true); #endregion } else { ICollection <Mapping> mappings; if (UseModifiedGrochow) { // Modified Mapping module - MODA and Grockow & Kellis mappings = Algorithm2_Modified(qGraph, inputGraph, numIterations, true); } else { mappings = Algorithm2(qGraph, inputGraph, numIterations, true); } qGraph.RemoveNonApplicableMappings(mappings, inputGraph); var fileName = $"{mappings.Count}#{qGraph.Identifier}.ser"; System.IO.File.WriteAllText(fileName, Extensions.CompressString(Newtonsoft.Json.JsonConvert.SerializeObject(mappings))); if (mappings.Count > 0) { mappings.Clear(); } allMappings = new Dictionary <QueryGraph, string>(1) { { qGraph, fileName } }; } return(allMappings); }
/// <summary> /// Algo 1: Find subgraph frequency (mappings help in memory) /// </summary> /// <param name="inputGraph"></param> /// <param name="qGraph">The query graph to be searched for. If not available, we use expansion trees (MODA). Otherwise, we use Grochow's (Algo 2)</param> /// <param name="subgraphSize"></param> /// <param name="thresholdValue">Frequency value, above which we can comsider the subgraph a "frequent subgraph"</param> /// <returns></returns> public static Dictionary <QueryGraph, ICollection <Mapping> > Algorithm1(UndirectedGraph <int> inputGraph, QueryGraph qGraph, int subgraphSize = -1, int thresholdValue = 0) { // The enumeration module (Algo 3) needs the mappings generated from the previous run(s) Dictionary <QueryGraph, ICollection <Mapping> > allMappings; int numIterations = -1; if (inputGraph.VertexCount < 121) { numIterations = inputGraph.VertexCount; } if (qGraph == null) // Use MODA's expansion tree { #region Use MODA's expansion tree var treatedNodes = new HashSet <QueryGraph>(); allMappings = new Dictionary <QueryGraph, ICollection <Mapping> >(_builder.NumberOfQueryGraphs); do { qGraph = GetNextNode()?.QueryGraph; if (qGraph == null) { break; } ICollection <Mapping> mappings; if (qGraph.IsTree(subgraphSize)) { if (UseModifiedGrochow) { // Modified Mapping module - MODA and Grockow & Kellis mappings = Algorithm2_Modified(qGraph, inputGraph, numIterations, false); } else { // Mapping module - MODA and Grockow & Kellis. var inputGraphClone = inputGraph.Clone(); mappings = Algorithm2(qGraph, inputGraphClone, numIterations, false); inputGraphClone.Clear(); inputGraphClone = null; } } else { // Enumeration moodule - MODA // This is part of Algo 3; but performance tweaks makes it more useful to get it here var parentQueryGraph = GetParent(qGraph, _builder.ExpansionTree); if (parentQueryGraph.IsTree(subgraphSize)) { treatedNodes.Add(parentQueryGraph); } string file; mappings = Algorithm3(allMappings, inputGraph, qGraph, _builder.ExpansionTree, parentQueryGraph, out file); } if (mappings != null && mappings.Count > thresholdValue) { qGraph.IsFrequentSubgraph = true; } // Save mappings. Do we need to save to disk? Maybe not! allMappings.Add(qGraph, mappings); // Do not call mappings.Clear() mappings = null; // Check for complete-ness; if complete, break if (qGraph.IsComplete(subgraphSize)) { qGraph = null; break; } qGraph = null; }while (true); if (treatedNodes.Count > 0) { foreach (var mapping in allMappings) { if (mapping.Key.IsTree(subgraphSize) && !treatedNodes.Contains(mapping.Key)) { mapping.Key.RemoveNonApplicableMappings(mapping.Value, inputGraph); } } treatedNodes.Clear(); } treatedNodes = null; #endregion } else { ICollection <Mapping> mappings; if (UseModifiedGrochow) { // Modified Mapping module - MODA and Grockow & Kellis mappings = Algorithm2_Modified(qGraph, inputGraph, numIterations, true); // mappings = ModaAlgorithm2Parallelized.Algorithm2_Modified(qGraph, inputGraph, numIterations); } else { mappings = Algorithm2(qGraph, inputGraph, numIterations, true); } qGraph.RemoveNonApplicableMappings(mappings, inputGraph); allMappings = new Dictionary <QueryGraph, ICollection <Mapping> >(1) { { qGraph, mappings } }; // Do not call mappings.Clear() mappings = null; } return(allMappings); }