Browse Source

Update of costfunctions hierarchy to distinguish cost function for ged and for cost matrix

v0.1
Benoit GAUZERE 7 years ago
parent
commit
d9dfc8ec68
4 changed files with 433 additions and 70 deletions
  1. +2
    -2
      ged/GED.py
  2. +2
    -2
      ged/bipartiteGED.py
  3. +36
    -31
      ged/costfunctions.py
  4. +393
    -35
      notebooks/py-graph_test.ipynb

+ 2
- 2
ged/GED.py View File

@@ -1,10 +1,10 @@
from ged.costfunctions import BasicCostFunction, RiesenCostFunction
from ged.costfunctions import ConstantCostFunction, RiesenCostFunction
from ged.costfunctions import NeighboorhoodCostFunction from ged.costfunctions import NeighboorhoodCostFunction
from ged.bipartiteGED import computeBipartiteCostMatrix, getOptimalMapping from ged.bipartiteGED import computeBipartiteCostMatrix, getOptimalMapping
from scipy.optimize import linear_sum_assignment from scipy.optimize import linear_sum_assignment


def ged(G1, G2, method='Riesen', rho=None, varrho=None, def ged(G1, G2, method='Riesen', rho=None, varrho=None,
cf=BasicCostFunction(1, 3, 1, 3),
cf=ConstantCostFunction(1, 3, 1, 3),
solver=linear_sum_assignment): solver=linear_sum_assignment):
"""Compute Graph Edit Distance between G1 and G2 according to mapping """Compute Graph Edit Distance between G1 and G2 according to mapping
encoded within rho and varrho. Graph's node must be indexed by a encoded within rho and varrho. Graph's node must be indexed by a


+ 2
- 2
ged/bipartiteGED.py View File

@@ -1,9 +1,9 @@
import numpy as np import numpy as np
from scipy.optimize import linear_sum_assignment from scipy.optimize import linear_sum_assignment
from ged.costfunctions import BasicCostFunction
from ged.costfunctions import ConstantCostFunction




def computeBipartiteCostMatrix(G1, G2, cf=BasicCostFunction(1, 3, 1, 3)):
def computeBipartiteCostMatrix(G1, G2, cf=ConstantCostFunction(1, 3, 1, 3)):
"""Compute a Cost Matrix according to cost function cf""" """Compute a Cost Matrix according to cost function cf"""
n = G1.number_of_nodes() n = G1.number_of_nodes()
m = G2.number_of_nodes() m = G2.number_of_nodes()


+ 36
- 31
ged/costfunctions.py View File

@@ -2,15 +2,17 @@ import numpy as np
from scipy.optimize import linear_sum_assignment from scipy.optimize import linear_sum_assignment




class BasicCostFunction:
class ConstantCostFunction:
""" Define a symmetric constant cost fonction for edit operations """
def __init__(self, cns, cni, ces, cei): def __init__(self, cns, cni, ces, cei):
self.cns_ = cns self.cns_ = cns
self.cni_ = self.cnd_ = cni self.cni_ = self.cnd_ = cni
self.ces_ = ces self.ces_ = ces
self.cei_ = self.ced_ = cei self.cei_ = self.ced_ = cei


def cns(self, u, v, G1, G2):
return (G1.node[u]['label'] != G2.node[v]['label'])*self.cns_
def cns(self, node_u, node_v, g1, g2):
""" return substitution edit operation cost between node_u of G1 and node_v of G2"""
return (g1.node[node_u]['label'] != g2.node[node_v]['label'])*self.cns_


def cnd(self, u, G1): def cnd(self, u, G1):
return self.cnd_ return self.cnd_
@@ -30,9 +32,11 @@ class BasicCostFunction:
return self.cei_ return self.cei_




class RiesenCostFunction(BasicCostFunction):
def __init__(self, cf):
BasicCostFunction.__init__(self, cf.cns_, cf.cni_, cf.ces_, cf.cei_)
class RiesenCostFunction():
""" Cost function associated to the computation of a cost matrix between nodes for LSAP"""
def __init__(self, cf, lsap_solver=linear_sum_assignment):
self.cf_ = cf
self.lsap_solver_ = lsap_solver


def cns(self, u, v, G1, G2): def cns(self, u, v, G1, G2):
""" u et v sont des id de noeuds """ """ u et v sont des id de noeuds """
@@ -48,41 +52,43 @@ class RiesenCostFunction(BasicCostFunction):
e1 = [u, nbr_u, G1[u][nbr_u]] e1 = [u, nbr_u, G1[u][nbr_u]]
for nbr_v in G2[v]: for nbr_v in G2[v]:
e2 = [v, nbr_v, G2[v][nbr_v]] e2 = [v, nbr_v, G2[v][nbr_v]]
sub_C[i, j] = self.ces(e1, e2, G1, G2)
sub_C[i, j] = self.cf_.ces(e1, e2, G1, G2)
j += 1 j += 1
i += 1 i += 1


i = 0 i = 0
for nbr_u in l_nbr_u: for nbr_u in l_nbr_u:
sub_C[i, m+i] = self.ced([u, nbr_u, G1[u][nbr_u]], G1)
sub_C[i, m+i] = self.cf_.ced([u, nbr_u, G1[u][nbr_u]], G1)
i += 1 i += 1


j = 0 j = 0
for nbr_v in l_nbr_v: for nbr_v in l_nbr_v:
sub_C[n+j, j] = self.cei([v, nbr_v, G2[v][nbr_v]], G2)
sub_C[n+j, j] = self.cf_.cei([v, nbr_v, G2[v][nbr_v]], G2)
j += 1 j += 1
row_ind, col_ind = linear_sum_assignment(sub_C)
row_ind, col_ind = self.lsap_solver_(sub_C)
cost = np.sum(sub_C[row_ind, col_ind]) cost = np.sum(sub_C[row_ind, col_ind])
return BasicCostFunction.cns(self, u, v, G1, G2) + cost
return self.cf_.cns(u, v, G1, G2) + cost


def cnd(self, u, G1): def cnd(self, u, G1):
cost = 0 cost = 0
for nbr in G1[u]: for nbr in G1[u]:
cost += BasicCostFunction.ced(self,[u,nbr,G1[u][nbr]],G1)
cost += self.cf_.ced([u,nbr,G1[u][nbr]],G1)
return BasicCostFunction.cnd(self,u,G1) + cost
return self.cf_.cnd(u,G1) + cost


def cni(self, v, G2): def cni(self, v, G2):
cost = 0 cost = 0
for nbr in G2[v]: for nbr in G2[v]:
cost += BasicCostFunction.cei(self, [v,nbr,G2[v][nbr]], G2)
cost += self.cf_.cei([v,nbr,G2[v][nbr]], G2)
return BasicCostFunction.cni(self, v, G2) + cost
return self.cf_.cni(v, G2) + cost




class NeighboorhoodCostFunction(BasicCostFunction):
def __init__(self, cf):
BasicCostFunction.__init__(self, cf.cns_, cf.cni_, cf.ces_, cf.cei_)
class NeighboorhoodCostFunction():
""" Cost function associated to the computation of a cost matrix between nodes for LSAP"""
def __init__(self, cf, lsap_solver=linear_sum_assignment):
self.cf_ = cf
self.lsap_solver_ = lsap_solver


def cns(self, u, v, G1, G2): def cns(self, u, v, G1, G2):
""" u et v sont des id de noeuds """ """ u et v sont des id de noeuds """
@@ -98,36 +104,35 @@ class NeighboorhoodCostFunction(BasicCostFunction):
e1 = [u, nbr_u, G1[u][nbr_u]] e1 = [u, nbr_u, G1[u][nbr_u]]
for nbr_v in G2[v]: for nbr_v in G2[v]:
e2 = [v, nbr_v, G2[v][nbr_v]] e2 = [v, nbr_v, G2[v][nbr_v]]
sub_C[i, j] = self.ces(e1, e2, G1, G2)
sub_C[i, j] += BasicCostFunction.cns(self,
nbr_u, nbr_v, G1, G2)
sub_C[i, j] = self.cf_.ces(e1, e2, G1, G2)
sub_C[i, j] += self.cf_.cns(nbr_u, nbr_v, G1, G2)
j += 1 j += 1
i += 1 i += 1


i = 0 i = 0
for nbr_u in l_nbr_u: for nbr_u in l_nbr_u:
sub_C[i, m+i] = self.ced([u, nbr_u, G1[u][nbr_u]], G1)
sub_C[i, m+i] += BasicCostFunction.cnd(self, nbr_u, G1)
sub_C[i, m+i] = self.cf_.ced([u, nbr_u, G1[u][nbr_u]], G1)
sub_C[i, m+i] += self.cf_.cnd(nbr_u, G1)
i += 1 i += 1


j = 0 j = 0
for nbr_v in l_nbr_v: for nbr_v in l_nbr_v:
sub_C[n+j, j] = self.cei([v, nbr_v, G2[v][nbr_v]], G2)
sub_C[n+j, j] += BasicCostFunction.cni(self, nbr_v, G2)
sub_C[n+j, j] = self.cf_.cei([v, nbr_v, G2[v][nbr_v]], G2)
sub_C[n+j, j] += self.cf_.cni(nbr_v, G2)
j += 1 j += 1


row_ind, col_ind = linear_sum_assignment(sub_C)
row_ind, col_ind = self.lsap_solver_(sub_C)
cost = np.sum(sub_C[row_ind, col_ind]) cost = np.sum(sub_C[row_ind, col_ind])
return BasicCostFunction.cns(self, u, v, G1, G2) + cost
return self.cf_.cns(u, v, G1, G2) + cost


def cnd(self, u, G1): def cnd(self, u, G1):
cost = 0 cost = 0
for nbr in G1[u]: for nbr in G1[u]:
cost += BasicCostFunction.ced(self, [u, nbr, G1[u][nbr]], G1)
return BasicCostFunction.cnd(self, u, G1) + cost
cost += self.cf_.ced([u, nbr, G1[u][nbr]], G1)
return self.cf_.cnd(u, G1) + cost


def cni(self, v, G2): def cni(self, v, G2):
cost = 0 cost = 0
for nbr in G2[v]: for nbr in G2[v]:
cost += BasicCostFunction.cei(self, [v, nbr, G2[v][nbr]], G2)
return BasicCostFunction.cni(self, v, G2) + cost
cost += self.cf_.cei([v, nbr, G2[v][nbr]], G2)
return self.cf_.cni(v, G2) + cost

+ 393
- 35
notebooks/py-graph_test.ipynb
File diff suppressed because it is too large
View File


Loading…
Cancel
Save