/// <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); }
/// <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> /// Mapping module; aka FindSubgraphInstances in Grochow & Kellis /// </summary> /// <param name="queryGraph">H</param> /// <param name="inputGraphClone">G</param> /// <param name="numberOfSamples">To be decided. If not set, we use the <paramref name="inputGraphClone"/> size / 3</param> internal static ICollection <Mapping> Algorithm2(QueryGraph queryGraph, UndirectedGraph <int> inputGraphClone, int numberOfSamples, bool getInducedMappingsOnly) { if (numberOfSamples <= 0) { numberOfSamples = inputGraphClone.VertexCount / 3; } // Do we need this clone? Can't we just remove the node directly from the graph? // We do need it. var theMappings = new Dictionary <int[], List <Mapping> >(MappingNodesComparer); var inputGraphDegSeq = inputGraphClone.GetNodesSortedByDegree(numberOfSamples); var queryGraphVertices = queryGraph.Vertices.ToArray(); var queryGraphEdges = queryGraph.Edges.ToArray(); var subgraphSize = queryGraphVertices.Length; var threadName = System.Threading.Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Thread {0}:\tCallingu Algo 2:\n", threadName); for (int i = 0; i < inputGraphDegSeq.Count; i++) { var g = inputGraphDegSeq[i]; for (int j = 0; j < subgraphSize; j++) { var h = queryGraphVertices[j]; if (Utils.CanSupport(queryGraph, h, inputGraphClone, g)) { #region Can Support //Remember: f(h) = g, so h is Domain and g is Range var f = new Dictionary <int, int>(1); f[h] = g; var mappings = Utils.IsomorphicExtension(f, queryGraph, queryGraphEdges, inputGraphClone, getInducedMappingsOnly); f.Clear(); f = null; if (mappings.Count > 0) { foreach (var item in mappings) { if (item.Value.Count > 1) { queryGraph.RemoveNonApplicableMappings(item.Value, inputGraphClone, getInducedMappingsOnly); } //Recall: f(h) = g List <Mapping> maps; if (theMappings.TryGetValue(item.Key, out maps)) { maps.AddRange(item.Value); } else { theMappings[item.Key] = item.Value; } } mappings.Clear(); } mappings = null; #endregion } } //Remove g inputGraphClone.RemoveVertex(g); if (inputGraphClone.EdgeCount == 0) { break; } } Array.Clear(queryGraphEdges, 0, queryGraphEdges.Length); queryGraphEdges = null; Array.Clear(queryGraphVertices, 0, subgraphSize); queryGraphVertices = null; inputGraphDegSeq.Clear(); inputGraphDegSeq = null; var toReturn = GetSet(theMappings); theMappings = null; Console.WriteLine("Thread {0}:\tAlgorithm 2: All tasks completed. Number of mappings found: {1}.", threadName, toReturn.Count); return(toReturn); }