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.bipartiteGED import computeBipartiteCostMatrix, getOptimalMapping
from scipy.optimize import linear_sum_assignment

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):
"""Compute Graph Edit Distance between G1 and G2 according to mapping
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
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"""
n = G1.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


class BasicCostFunction:
class ConstantCostFunction:
""" Define a symmetric constant cost fonction for edit operations """
def __init__(self, cns, cni, ces, cei):
self.cns_ = cns
self.cni_ = self.cnd_ = cni
self.ces_ = ces
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):
return self.cnd_
@@ -30,9 +32,11 @@ class BasicCostFunction:
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):
""" u et v sont des id de noeuds """
@@ -48,41 +52,43 @@ class RiesenCostFunction(BasicCostFunction):
e1 = [u, nbr_u, G1[u][nbr_u]]
for nbr_v in G2[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
i += 1

i = 0
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

j = 0
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
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])
return BasicCostFunction.cns(self, u, v, G1, G2) + cost
return self.cf_.cns(u, v, G1, G2) + cost

def cnd(self, u, G1):
cost = 0
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):
cost = 0
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):
""" u et v sont des id de noeuds """
@@ -98,36 +104,35 @@ class NeighboorhoodCostFunction(BasicCostFunction):
e1 = [u, nbr_u, G1[u][nbr_u]]
for nbr_v in G2[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
i += 1

i = 0
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

j = 0
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

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])
return BasicCostFunction.cns(self, u, v, G1, G2) + cost
return self.cf_.cns(u, v, G1, G2) + cost

def cnd(self, u, G1):
cost = 0
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):
cost = 0
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