/** * */ package edu.isi.karma.modeling.research.graphmatching.util; import java.util.LinkedList; import java.util.TreeSet; /** * @author riesen * */ public class EditDistance { /** * whether or not the edges are directed */ @SuppressWarnings("unused") private boolean undirected; /** * whether or not the editpath found is outputted on the console */ private int outputEditpath; /** * constructor * @param undirected * @param outputEditpath * @param bipartiteMatching * @param matrixGenerator */ public EditDistance(int undirected, int outputEditpath) { if (undirected == 1){ this.undirected = true; } else { this.undirected = false; } this.outputEditpath = outputEditpath; } /** * * @return the approximated edit distance between graph @param g1 * and graph @param g2 according to the @param matching using the cost function @param cf */ public double getEditDistance(Graph g1, Graph g2, int[][] matching, CostFunction cf) { // if the edges are undirected // all of the edge operations have to be multiplied by 0.5 // since all edge operations are performed twice double factor = 1.0; if (this.undirected = true){ factor = 0.5; } if (this.outputEditpath ==1){ System.out.println("\nThe Edit Path:"); } double ed = 0.; // the edges of graph g1 and graph g2 Edge[][] edgesOfG1 = g1.getAdjacenyMatrix(); Edge[][] edgesOfG2 = g2.getAdjacenyMatrix(); for (int i = 0; i < matching.length; i++){ if (matching[i][0] < g1.size()){ if (matching[i][1] < g2.size()){ // i-th node substitution with node from g2 with index matching[i][1] ed += cf.getCost(g1.get(matching[i][0]), g2.get(matching[i][1])); if (this.outputEditpath ==1){ System.out.println(g1.get(matching[i][0]).getNodeID()+"\t-->\t"+ g2.get(matching[i][1]).getNodeID()+"\t"+cf.getCost(g1.get(matching[i][0]), g2.get(matching[i][1]))); } // edge handling when i-th node is substituted with node with index matching[i][i]; // iterating through all possible edges e_ij of node i for (int j = 0; j < matching.length; j++){ if (j < edgesOfG1[0].length){ Edge e_ij = edgesOfG1[matching[i][0]][j]; if (matching[j][1] < g2.size()){ // node with index j is NOT deleted but subtituted Edge e_ij_mapped = edgesOfG2[matching[i][1]][matching[j][1]]; if (e_ij != null){ if (e_ij_mapped != null){ // case 1: // there is an edge between the i-th and j-th node (e_ij) // AND there is an edge between the mappings of the i-th and j-th node (e_ij_mapped) ed += factor * cf.getCost(e_ij, e_ij_mapped); // substitute the edge if (this.outputEditpath==1){ System.out.println(e_ij.getEdgeID() +"\t-->\t"+e_ij_mapped.getEdgeID()+"\t"+factor * cf.getCost(e_ij, e_ij_mapped)); } } else { // case 2: // there is an edge between the i-th and j-th node (e_ij) // BUT there is NO edge between the mappings of the i-th and j-th node (e_ij_mapped=null) ed += factor * cf.getEdgeCosts(); // delete the edge if (this.outputEditpath==1){ System.out.println(e_ij.getEdgeID()+"\t-->\teps\t"+factor * cf.getEdgeCosts()); } } } else { if (e_ij_mapped != null){ // case 3: // there is NO edge between the i-th and j-th node // BUT there is an edge between the mappings if (this.outputEditpath==1){ System.out.println("eps\t-->\t"+e_ij_mapped.getEdgeID()+"\t"+factor * cf.getEdgeCosts()); } ed += factor * cf.getEdgeCosts(); // insert the edge } } } else { // node with index j is deleted if (e_ij != null){ // case 4: // there is an edge between the i-th and j-th node and the j-th node is deleted ed += factor * cf.getEdgeCosts(); // delete the edge if (this.outputEditpath==1){ System.out.println(e_ij.getEdgeID()+"\t-->\teps\t"+factor * cf.getEdgeCosts()); } } } } else { // node with index j is inserted if (matching[j][1] < g2.size()){ Edge e_ij_mapped = edgesOfG2[matching[i][1]][matching[j][1]]; if (e_ij_mapped != null){ // case 5: // there is an edge between the mappings of i and j if (this.outputEditpath==1){ System.out.println("eps\t-->\t"+e_ij_mapped.getEdgeID()+"\t"+factor * cf.getEdgeCosts()); } ed += factor * cf.getEdgeCosts(); // insert the edge } } } } } else{ // i-th node deletion ed += cf.getNodeCosts(); if (this.outputEditpath==1){ System.out.println(g1.get(matching[i][0]).getNodeID()+"\t-->\t"+ "eps\t"+cf.getNodeCosts()); } // edge handling for (int j = 0; j < edgesOfG1[0].length; j++){ Edge e_ij = edgesOfG1[matching[i][0]][j]; if (e_ij != null){ // case 6: // there is an edge between the i-th and j-th node ed += factor * cf.getEdgeCosts(); // delete the edge if (this.outputEditpath==1){ System.out.println(e_ij.getEdgeID()+"\t-->\teps\t"+ factor *cf.getEdgeCosts()); } } } } } else{ if (matching[i][1] < g2.size()){ // i-th node insertion ed += cf.getNodeCosts(); if (this.outputEditpath==1){ System.out.println("eps\t-->\t"+ g2.get(matching[i][1]).getNodeID()+"\t"+cf.getNodeCosts()); } // edge handling for (int j = 0; j < edgesOfG2[0].length; j++){ Edge e_ij_mapped = edgesOfG2[matching[i][1]][j]; if (e_ij_mapped != null){ // case 7: // there is an edge between the mapping of the i-th and j-th node ed += factor * cf.getEdgeCosts(); // insert the edge if (this.outputEditpath==1){ System.out.println("eps\t-->\t"+e_ij_mapped.getEdgeID()+"\t"+ factor *cf.getEdgeCosts()); } } } } } } if (this.outputEditpath==1){ System.out.println("The Edit Distance: "+ed); } return ed; } /** * * @return the exact edit distance between graph @param g1 * and graph @param g2 using the cost function @param cf * s is the maximum number of open paths used in beam-search */ public double getEditDistance(Graph g1, Graph g2, CostFunction cf, int s) { // list of partial edit paths (open) organized as TreeSet TreeSet<TreeNode> open = new TreeSet<TreeNode>(); // if the edges are undirected // all of the edge operations have to be multiplied by 0.5 // since all edge operations are performed twice double factor = 1.0; if (this.undirected = true){ factor = 0.5; } // each treenode represents a (partial) solution (i.e. edit path) // start is the first (empty) partial solution TreeNode start = new TreeNode(g1, g2, cf, factor); open.add(start); // main loop of the tree search while (!open.isEmpty()){ TreeNode u = open.pollFirst(); if (u.allNodesUsed()){ return u.getCost(); } // generates all successors of node u in the search tree // and add them to open LinkedList<TreeNode> successors = u.generateSuccessors(); open.addAll(successors); // in beam search the maximum number of open paths // is limited to s while (open.size() > s){ open.pollLast(); } } // error case return -1; } }