/** The initiating call should be to the root node of the tree. * It fills in an nxn (hash) table of the leftmost leaf for a * given node. It also compiles an array of key roots. The * int values IDs must come from the post-ordering of the * nodes in the tree. */ private void findHelperTables(TreeDefinition someTree, Dictionary<int, int> leftmostLeaves, List<int> keyroots, int aNodeID) { findHelperTablesRecurse(someTree, leftmostLeaves, keyroots, aNodeID); //add root to keyroots keyroots.Add(aNodeID); //add boundary nodes leftmostLeaves[0] = 0; }
private void findHelperTablesRecurse(TreeDefinition someTree, Dictionary<int, int> leftmostLeaves, List<int> keyroots, int aNodeID) { //If this is a leaf, then it is the leftmost leaf if (someTree.isLeaf(aNodeID)) { leftmostLeaves[aNodeID] = aNodeID; } else { bool seenLeftmost = false; foreach (int child in someTree.getChildrenIDs(aNodeID)) { findHelperTablesRecurse(someTree, leftmostLeaves, keyroots, child); if (!seenLeftmost) { leftmostLeaves[aNodeID] = leftmostLeaves[child]; seenLeftmost = true; } else keyroots.Add(child); } } }
public Transformation getTransformation(TreeDefinition aTree, TreeDefinition bTree) { Transformation transform = new Transformation(); Transformation t1 = findDistance(aTree, bTree); transform.totalCost = distance[aTree.getNodeCount(), bTree.getNodeCount()]; Transformation t2 = findDistance(bTree, aTree); transform.editScriptAtoB = t1.editScriptAtoB; transform.editScriptBtoA = t2.editScriptAtoB; transform.pdlA = t1.pdlA; transform.pdlB = t1.pdlB; transform.pdlMappingTreeB = t1.pdlMappingTreeB; transform.pdlMappingTreeA = t1.pdlMappingTreeA; return transform; }
//Computes tree edits distance betwee aTree and bTree public Transformation findDistance(TreeDefinition aTree, TreeDefinition bTree) { distance = new double[aTree.getNodeCount() + 1, bTree.getNodeCount() + 1]; distScript = new TreeEditScript[aTree.getNodeCount() + 1, bTree.getNodeCount() + 1]; //Preliminaries //1. Find left-most leaf and key roots Dictionary<int, int> aLeftLeaf = new Dictionary<int, int>(); Dictionary<int, int> bLeftLeaf = new Dictionary<int, int>(); List<int> aTreeKeyRoots = new List<int>(); List<int> bTreeKeyRoots = new List<int>(); findHelperTables(aTree, aLeftLeaf, aTreeKeyRoots, aTree.getRootID()); findHelperTables(bTree, bLeftLeaf, bTreeKeyRoots, bTree.getRootID()); var a = bTree.getRootID(); //Comparison foreach (int aKeyroot in aTreeKeyRoots) { foreach (int bKeyroot in bTreeKeyRoots) { //Re-initialise forest distance tables Dictionary<int, Dictionary<int, Double>> fD = new Dictionary<int, Dictionary<int, Double>>(); //Re-initialise forest edit script distance tables Dictionary<int, Dictionary<int, TreeEditScript>> fESD = new Dictionary<int, Dictionary<int, TreeEditScript>>(); setForestDistance(aLeftLeaf[aKeyroot], bLeftLeaf[bKeyroot], 0.0d, fD); //script is automatically null //for all descendents of aKeyroot: i for (int i = aLeftLeaf[aKeyroot]; i <= aKeyroot; i++) { var edit = new Delete(i,aTree); setForestDistance(i, bLeftLeaf[bKeyroot] - 1, getForestDistance(i - 1, bLeftLeaf[bKeyroot] - 1, fD) + edit.getCost(), fD); var scriptSuffix = new TreeEditScript(getForestScript(i - 1, bLeftLeaf[bKeyroot] - 1, fESD)); scriptSuffix.Insert(edit); setForestScript(i, bLeftLeaf[bKeyroot] - 1, scriptSuffix, fESD); } //for all descendents of bKeyroot: j for (int j = bLeftLeaf[bKeyroot]; j <= bKeyroot; j++) { var edit = new Insert(j, bTree); setForestDistance(aLeftLeaf[aKeyroot] - 1, j, getForestDistance(aLeftLeaf[aKeyroot] - 1, j - 1, fD) + edit.getCost(), fD); var scriptSuffix = new TreeEditScript(getForestScript(aLeftLeaf[aKeyroot] - 1, j - 1, fESD)); scriptSuffix.Insert(edit); setForestScript(aLeftLeaf[aKeyroot] - 1, j, scriptSuffix, fESD); } for (int i = aLeftLeaf[aKeyroot]; i <= aKeyroot; i++) { for (int j = bLeftLeaf[bKeyroot]; j <= bKeyroot; j++) { TreeEditScript tempScript = null; EditOperation delEdit = new Delete(i, aTree); ; EditOperation insEdit = new Insert(j, bTree); double min; double delCost = getForestDistance(i - 1, j, fD) + delEdit.getCost(); double insCost = getForestDistance(i, j - 1, fD) + insEdit.getCost(); //This min compares del vs ins if (delCost <= insCost) { //Option 1: Delete node from aTree tempScript = new TreeEditScript(getForestScript(i - 1, j, fESD)); tempScript.Insert(delEdit); min = delCost; } else { //Option 2: Insert node into bTree tempScript = new TreeEditScript(getForestScript(i, j - 1, fESD)); tempScript.Insert(insEdit); min = insCost; } if (aLeftLeaf[i] == aLeftLeaf[aKeyroot] && bLeftLeaf[j] == bLeftLeaf[bKeyroot]) { var renEdit = new Rename(i, j, aTree, bTree); var dist = getForestDistance(i - 1, j - 1, fD) + renEdit.getCost(); distance[i, j] = Math.Min(min, dist); if (min <= dist) { tempScript = new TreeEditScript(tempScript); distScript[i, j] = tempScript; } else { tempScript = new TreeEditScript(getForestScript(i - 1, j - 1, fESD)); tempScript.Insert(renEdit); distScript[i, j] = tempScript; } setForestDistance(i, j, distance[i, j], fD); setForestScript(i, j, new TreeEditScript(distScript[i, j]), fESD); } else { var value = getForestDistance(aLeftLeaf[i] - 1, bLeftLeaf[j] - 1, fD) + distance[i, j]; setForestDistance(i, j, Math.Min(min, value), fD); if (min <= value) setForestScript(i, j, new TreeEditScript(tempScript), fESD); else { var tempList = getForestScript(aLeftLeaf[i] - 1, bLeftLeaf[j] - 1, fESD).script; tempList = distScript[i, j].script.Concat(tempList).ToList(); setForestScript(i, j, new TreeEditScript(tempList), fESD); } setForestDistance(i, j, Math.Min(min, value), fD); } } } } } Transformation transform = new Transformation(); transform.totalCost = distance[aTree.getNodeCount(), bTree.getNodeCount()]; transform.editScriptAtoB = distScript[aTree.getNodeCount(), bTree.getNodeCount()]; transform.pdlA = aTree.pdl; transform.pdlB = bTree.pdl; transform.pdlMappingTreeB = bTree.pdlNodeMapping; transform.pdlMappingTreeA = aTree.pdlNodeMapping; return transform; }
public Rename(int aNodeId, int bNodeId, TreeDefinition aTree, TreeDefinition bTree) { labelA = aTree.getLabelForMatching(aNodeId); labelB = bTree.getLabelForMatching(bNodeId); pdlA = aTree.pdlNodeMapping[labelA]; pdlB = bTree.pdlNodeMapping[labelB]; int aDiv = labelA.LastIndexOf(":"); if (aDiv == -1) throw new PDLException(string.Format("All nodes should have a : in the middle: {0}", labelA)); int bDiv = labelB.LastIndexOf(":"); if (bDiv == -1) throw new PDLException(string.Format("All nodes should have a : in the middle: {0}", labelB)); labelA = labelA.Substring(0, aDiv); labelB = labelB.Substring(0, bDiv); }
public Insert(int bNodeId, TreeDefinition bTree) { labelB = bTree.getLabelForMatching(bNodeId); pdlB = bTree.pdlNodeMapping[labelB]; int div = labelB.LastIndexOf(":"); if (div == -1) throw new PDLException(string.Format("All nodes should have a : in the middle: {0}", labelB)); labelB = labelB.Substring(0, div); }
public Delete(int aNodeId, TreeDefinition aTree) { labelA = aTree.getLabelForMatching(aNodeId); pdlA = aTree.pdlNodeMapping[labelA]; int div = labelA.LastIndexOf(":"); if (div == -1) throw new PDLException(string.Format("All nodes should have a : in the middle: {0}", labelA)); labelA = labelA.Substring(0, div); }