/// <summary> /// Enumeration module. NB: If either of <paramref name="allMappings"/> or <paramref name="fileName"/> is null, the other will not be. /// </summary> /// <param name="allMappings"></param> /// <param name="inputGraph">G</param> /// <param name="queryGraph">H</param> /// <param name="expansionTree">T_k</param> /// <param name="parentQueryGraph"></param> /// <param name="fileName"></param> /// <param name="parentGraphMappings">NB: This param is still used even outside this method is call. So, be careful how you set/clear its values.</param> private static IList <Mapping> Algorithm3(Dictionary <QueryGraph, ICollection <Mapping> > allMappings, UndirectedGraph <int> inputGraph, QueryGraph queryGraph, AdjacencyGraph <ExpansionTreeNode> expansionTree, QueryGraph parentQueryGraph, out string newFileName, string fileName = null) { newFileName = null; ICollection <Mapping> parentGraphMappings; if (string.IsNullOrWhiteSpace(fileName)) { if (!allMappings.TryGetValue(parentQueryGraph, out parentGraphMappings)) { return(new Mapping[0]); } } else { parentGraphMappings = parentQueryGraph.ReadMappingsFromFile(fileName); } if (parentGraphMappings.Count == 0) { return(new Mapping[0]); } var subgraphSize = queryGraph.VertexCount; var parentQueryGraphEdges = new HashSet <Edge <int> >(); foreach (var edge in parentQueryGraph.Edges) { parentQueryGraphEdges.Add(edge); } var newEdge = GetEdgeDifference(queryGraph, parentQueryGraph, parentQueryGraphEdges); parentQueryGraphEdges.Clear(); parentQueryGraphEdges = null; // if it's NOT a valid edge if (newEdge.Source == Utils.DefaultEdgeNodeVal) { return(new Mapping[0]); } var list = new List <Mapping>(); int oldCount = parentGraphMappings.Count, id = 0, queryGraphEdgeCount = queryGraph.EdgeCount; var queryGraphEdges = queryGraph.Edges.ToArray(); var groupByGNodes = parentGraphMappings.GroupBy(x => x.Function.Values.ToArray(), MappingNodesComparer); //.ToDictionary(x => x.Key, x => x.ToArray(), MappingNodesComparer); foreach (var set in groupByGNodes) { // function.value (= set of G nodes) are all same here. So build the subgraph here and pass it dowm var subgraph = Utils.GetSubgraph(inputGraph, set.Key); foreach (var item in set) { item.Id = id++; // Remember, f(h) = g // if (f(u), f(v)) ϵ G and meets the conditions, add to list if (item.SubGraphEdgeCount == queryGraphEdgeCount) { var isMapping = Utils.IsMappingCorrect2(item.Function, subgraph, queryGraphEdges, true); if (isMapping.IsCorrectMapping) { list.Add(item); } isMapping = null; } else if (item.SubGraphEdgeCount > queryGraphEdgeCount) { var newEdgeImage = item.GetImage(inputGraph, newEdge); // if it's a valid edge... if (newEdgeImage.Source != Utils.DefaultEdgeNodeVal && inputGraph.ContainsEdge(newEdgeImage.Source, newEdgeImage.Target)) { list.Add(item); } } } subgraph = null; } Array.Clear(queryGraphEdges, 0, queryGraphEdges.Length); queryGraphEdges = null; var threadName = System.Threading.Thread.CurrentThread.ManagedThreadId; // Remove mappings from the parent qGraph that are found in this qGraph // This is because we're only interested in induced subgraphs var theRest = parentGraphMappings.Except(list).ToList(); parentQueryGraph.RemoveNonApplicableMappings(theRest, inputGraph); parentGraphMappings.Clear(); foreach (var item in theRest) { parentGraphMappings.Add(item); } theRest.Clear(); theRest = null; // Now, remove duplicates queryGraph.RemoveNonApplicableMappings(list, inputGraph); if (!string.IsNullOrWhiteSpace(fileName) && oldCount > parentGraphMappings.Count) { // This means that some of the mappings from parent fit the current query graph newFileName = parentQueryGraph.WriteMappingsToFile(parentGraphMappings); try { System.IO.File.Delete(fileName); } catch { } // we can afford to let this fail } Console.WriteLine("Thread {0}:\tAlgorithm 3: All tasks completed. Number of mappings found: {1}.\n", threadName, list.Count); return(list); }
/// <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); }