/**
*
*/
package edu.isi.karma.modeling.research.graphmatching.util;
/**
* @author riesen
*
*/
public class CostFunction {
/**
* the constant cost for node and edge deletions/insertions
*/
private double nodeCost;
private double edgeCost;
/**
* the weighting factor alpha measures the relative importance of node and
* edge operations
*/
private double alpha;
/**
* all identifiers (names) of node and edge attributes
*/
private String[] nodeAttributes;
private String[] edgeAttributes;
/**
* all types of node and edge cost functions
*/
private String[] nodeCostTypes;
private String[] edgeCostTypes;
/**
* the weighting factors for the node and edge attributes
*/
private double[] nodeAttrImportance;
private double[] edgeAttrImportance;
/**
* combined costs are "p-rooted"
*/
private double pNode;
private double pEdge;
/**
* whether the individual costs are multiplied (=1) instead of added
*/
private int multiplyNodeCosts;
private int multiplyEdgeCosts;
/**
* mu and nu are non-negative real values to be defined by the user if cost-function=discrete
* mu = cost for substitution of equal labels
* nu = cost for substitution of unequal labels
*/
private double[] nodeCostMu;
private double[] nodeCostNu;
/**
* initializes the costfunction according to the properties read in the properties file
*/
public CostFunction(double n, double e, double a, String[] nodeAttributes,
String[] nodeCostTypes, double[] nodeAttrImportance,
String[] edgeAttributes, String[] edgeCostTypes,
double[] edgeAttrImportance, double pNode,
int multiplyNodeCosts, double pEdge,
int multiplyEdgeCosts, double[] nodeCostMu, double[] nodeCostNu) {
this.nodeCost = n;
this.edgeCost = e;
this.alpha = a;
this.nodeAttributes = nodeAttributes;
this.nodeCostTypes = nodeCostTypes;
this.nodeAttrImportance = nodeAttrImportance;
this.edgeAttributes = edgeAttributes;
this.edgeCostTypes = edgeCostTypes;
this.edgeAttrImportance = edgeAttrImportance;
this.pNode = pNode;
this.multiplyNodeCosts = multiplyNodeCosts;
this.pEdge = pEdge;
this.multiplyEdgeCosts = multiplyEdgeCosts;
this.nodeCostMu = nodeCostMu;
this.nodeCostNu = nodeCostNu;
}
/**
* @return the substitution cost between node @param u
* and node @param v according to their attribute values and the cost functions.
* The individual costs are softened by the importance factors and finally
* added or multiplied (and possibly the result is "square rooted")
* The final result is multiplied by alpha
*/
public double getCost(Node u, Node v) {
double cost = 0;
for (int i = 0; i < this.nodeAttributes.length; i++) {
if (this.nodeCostTypes[i].equals("squared")) {
double u_x = Double.parseDouble(u
.getValue(this.nodeAttributes[i]));
double v_x = Double.parseDouble(v
.getValue(this.nodeAttributes[i]));
if (this.multiplyNodeCosts == 1) {
cost *= Math.pow((u_x - v_x), 2.)
* this.nodeAttrImportance[i];
} else {
cost += (Math.pow((u_x - v_x), 2.)
* this.nodeAttrImportance[i]);
}
}
if (this.nodeCostTypes[i].equals("absolute")) {
double u_x = Double.parseDouble(u
.getValue(this.nodeAttributes[i]));
double v_x = Double.parseDouble(v
.getValue(this.nodeAttributes[i]));
if (this.multiplyNodeCosts == 1) {
cost *= Math.abs((u_x - v_x)) * this.nodeAttrImportance[i];
} else {
cost += (Math.abs((u_x - v_x)) * this.nodeAttrImportance[i]);
}
}
if (this.nodeCostTypes[i].equals("discrete")) {
String u_x = u.getValue(this.nodeAttributes[i]);
String v_x = v.getValue(this.nodeAttributes[i]);
if (u_x.equals(v_x)) {
if (this.multiplyNodeCosts == 1) {
cost *= this.nodeCostMu[i]* this.nodeAttrImportance[i];
} else {
cost += (this.nodeCostMu[i]* this.nodeAttrImportance[i]);
}
} else {
if (this.multiplyNodeCosts == 1) {
cost *= this.nodeCostNu[i] * this.nodeAttrImportance[i];
} else {
cost += (this.nodeCostNu[i] * this.nodeAttrImportance[i]);
}
}
}
if (this.nodeCostTypes[i].equals("sed")) {
String u_x = u.getValue(this.nodeAttributes[i]);
String v_x = v.getValue(this.nodeAttributes[i]);
if (this.multiplyNodeCosts == 1) {
cost *= this.stringEditDistance(u_x, v_x)
* this.nodeAttrImportance[i];
} else {
cost += (this.stringEditDistance(u_x, v_x)
* this.nodeAttrImportance[i]);
}
}
}
cost = Math.pow(cost, (1/this.pNode));
cost *= this.alpha;
return cost;
}
/**
* @return the substitution cost between edge @param u
* and edge @param v according to their attribute values and the cost functions.
* The individual costs are softened by the importance factors and finally
* added or multiplied (and possibly the result is "square rooted")
* The final result is multiplied by (1-alpha)
*/
public double getCost(Edge u, Edge v) {
double cost = 0;
for (int i = 0; i < this.edgeAttributes.length; i++) {
if (this.edgeCostTypes[i].equals("squared")) {
double u_x = Double.parseDouble(u
.getValue(this.edgeAttributes[i]));
double v_x = Double.parseDouble(v
.getValue(this.edgeAttributes[i]));
if (this.multiplyEdgeCosts == 1) {
cost *= Math.pow((u_x - v_x), 2.)
* this.edgeAttrImportance[i];
} else {
cost += Math.pow((u_x - v_x), 2.)
* this.edgeAttrImportance[i];
}
}
if (this.edgeCostTypes[i].equals("absolute")) {
double u_x = Double.parseDouble(u
.getValue(this.edgeAttributes[i]));
double v_x = Double.parseDouble(v
.getValue(this.edgeAttributes[i]));
if (this.multiplyEdgeCosts == 1) {
cost *= Math.abs((u_x - v_x)) * this.edgeAttrImportance[i];
} else {
cost += Math.abs((u_x - v_x)) * this.edgeAttrImportance[i];
}
}
if (this.edgeCostTypes[i].equals("equality")) {
String u_x = u.getValue(this.edgeAttributes[i]);
String v_x = v.getValue(this.edgeAttributes[i]);
/**
* Added by Mohsen
*/
String sId1 = u.getStartNode().getValue(this.nodeAttributes[0]);
String tId1 = u.getEndNode().getValue(this.nodeAttributes[0]);
String sId2 = v.getStartNode().getValue(this.nodeAttributes[0]);
String tId2 = v.getEndNode().getValue(this.nodeAttributes[0]);
if (u_x.equals(v_x) && sId1.equals(sId2) && tId1.equals(tId2)) {
/**
* Commented by Mohsen
*/
// if (u_x.equals(v_x)) {
if (this.multiplyEdgeCosts == 1) {
cost *= 0;
} else {
cost += 0;
}
} else {
if (this.multiplyEdgeCosts == 1) {
cost *= 1 * this.edgeAttrImportance[i];
} else {
cost += 1 * this.edgeAttrImportance[i];
}
}
}
if (this.edgeCostTypes[i].equals("sed")) {
String u_x = u.getValue(this.edgeAttributes[i]);
String v_x = v.getValue(this.edgeAttributes[i]);
if (this.multiplyEdgeCosts == 1) {
cost *= this.stringEditDistance(u_x, v_x)
* this.edgeAttrImportance[i];
} else {
cost += this.stringEditDistance(u_x, v_x)
* this.edgeAttrImportance[i];
}
}
}
cost = Math.pow(cost, (1/this.pEdge));
cost *= (1 - this.alpha);
return cost;
}
/**
* @return the string edit distance between strings
* @param s1 and @param s2
*
*/
private double stringEditDistance(String s1, String s2) {
int n = s1.length();
int m = s2.length();
double[][] stringMatrix = new double[n + 1][m + 1];
stringMatrix[0][0] = 0;
for (int i = 1; i <= n; i++) {
stringMatrix[i][0] = stringMatrix[i - 1][0] + 1.;
}
for (int j = 1; j <= m; j++) {
stringMatrix[0][j] = stringMatrix[0][j - 1] + 1.;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
double subst = 0.;
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
subst = 0.;
} else {
subst = 2;
}
double m1 = stringMatrix[i - 1][j - 1] + subst;
double m2 = stringMatrix[i - 1][j] + 1;
double m3 = stringMatrix[i][j - 1] + 1;
stringMatrix[i][j] = Math.min(m1, Math.min(m2, m3));
}
}
return stringMatrix[n][m];
}
/**
* @return the constant cost for node deletion/insertion
* multiplied by alpha
*/
public double getNodeCosts() {
return this.alpha * this.nodeCost;
}
/**
* @return the constant cost for edge deletion/insertion
* multiplied by (1-alpha)
*/
public double getEdgeCosts() {
return (1 - this.alpha) * this.edgeCost;
}
}