|
@@ -0,0 +1,117 @@ |
|
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
""" |
|
|
|
|
|
Created on Thu Jun 18 16:09:29 2020 |
|
|
|
|
|
|
|
|
|
|
|
@author: ljia |
|
|
|
|
|
""" |
|
|
|
|
|
import numpy as np |
|
|
|
|
|
import networkx as nx |
|
|
|
|
|
from gklearn.ged.methods import LSAPEBasedMethod |
|
|
|
|
|
from gklearn.ged.util import LSAPESolver |
|
|
|
|
|
from gklearn.utils import SpecialLabel |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Bipartite(LSAPEBasedMethod): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, ged_data): |
|
|
|
|
|
super().__init__(ged_data) |
|
|
|
|
|
self._compute_lower_bound = False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################### |
|
|
|
|
|
# Inherited member functions from LSAPEBasedMethod. |
|
|
|
|
|
########################################################################### |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _lsape_populate_instance(self, g, h, master_problem): |
|
|
|
|
|
# #ifdef _OPENMP |
|
|
|
|
|
for row_in_master in range(0, nx.number_of_nodes(g)): |
|
|
|
|
|
for col_in_master in range(0, nx.number_of_nodes(h)): |
|
|
|
|
|
master_problem[row_in_master, col_in_master] = self._compute_substitution_cost(g, h, row_in_master, col_in_master) |
|
|
|
|
|
for row_in_master in range(0, nx.number_of_nodes(g)): |
|
|
|
|
|
master_problem[row_in_master, nx.number_of_nodes(h) + row_in_master] = self._compute_deletion_cost(g, row_in_master) |
|
|
|
|
|
for col_in_master in range(0, nx.number_of_nodes(h)): |
|
|
|
|
|
master_problem[nx.number_of_nodes(g) + col_in_master, col_in_master] = self._compute_insertion_cost(h, col_in_master) |
|
|
|
|
|
|
|
|
|
|
|
# for row_in_master in range(0, master_problem.shape[0]): |
|
|
|
|
|
# for col_in_master in range(0, master_problem.shape[1]): |
|
|
|
|
|
# if row_in_master < nx.number_of_nodes(g) and col_in_master < nx.number_of_nodes(h): |
|
|
|
|
|
# master_problem[row_in_master, col_in_master] = self._compute_substitution_cost(g, h, row_in_master, col_in_master) |
|
|
|
|
|
# elif row_in_master < nx.number_of_nodes(g): |
|
|
|
|
|
# master_problem[row_in_master, nx.number_of_nodes(h)] = self._compute_deletion_cost(g, row_in_master) |
|
|
|
|
|
# elif col_in_master < nx.number_of_nodes(h): |
|
|
|
|
|
# master_problem[nx.number_of_nodes(g), col_in_master] = self._compute_insertion_cost(h, col_in_master) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################### |
|
|
|
|
|
# Helper member functions. |
|
|
|
|
|
########################################################################### |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _compute_substitution_cost(self, g, h, u, v): |
|
|
|
|
|
# Collect node substitution costs. |
|
|
|
|
|
cost = self._ged_data.node_cost(g.nodes[u]['label'], h.nodes[v]['label']) |
|
|
|
|
|
|
|
|
|
|
|
# Initialize subproblem. |
|
|
|
|
|
d1, d2 = g.degree[u], h.degree[v] |
|
|
|
|
|
subproblem = np.ones((d1 + d2, d1 + d2)) * np.inf |
|
|
|
|
|
subproblem[d1:, d2:] = 0 |
|
|
|
|
|
# subproblem = np.empty((g.degree[u] + 1, h.degree[v] + 1)) |
|
|
|
|
|
|
|
|
|
|
|
# Collect edge deletion costs. |
|
|
|
|
|
i = 0 # @todo: should directed graphs be considered? |
|
|
|
|
|
for label in g[u].values(): # all u's neighbor |
|
|
|
|
|
subproblem[i, d2 + i] = self._ged_data.edge_cost(label['label'], SpecialLabel.DUMMY) |
|
|
|
|
|
# subproblem[i, h.degree[v]] = self._ged_data.edge_cost(label['label'], SpecialLabel.DUMMY) |
|
|
|
|
|
i += 1 |
|
|
|
|
|
|
|
|
|
|
|
# Collect edge insertion costs. |
|
|
|
|
|
i = 0 # @todo: should directed graphs be considered? |
|
|
|
|
|
for label in h[v].values(): # all u's neighbor |
|
|
|
|
|
subproblem[d1 + i, i] = self._ged_data.edge_cost(SpecialLabel.DUMMY, label['label']) |
|
|
|
|
|
# subproblem[g.degree[u], i] = self._ged_data.edge_cost(SpecialLabel.DUMMY, label['label']) |
|
|
|
|
|
i += 1 |
|
|
|
|
|
|
|
|
|
|
|
# Collect edge relabelling costs. |
|
|
|
|
|
i = 0 |
|
|
|
|
|
for label1 in g[u].values(): |
|
|
|
|
|
j = 0 |
|
|
|
|
|
for label2 in h[v].values(): |
|
|
|
|
|
subproblem[i, j] = self._ged_data.edge_cost(label1['label'], label2['label']) |
|
|
|
|
|
j += 1 |
|
|
|
|
|
i += 1 |
|
|
|
|
|
|
|
|
|
|
|
# Solve subproblem. |
|
|
|
|
|
subproblem_solver = LSAPESolver(subproblem) |
|
|
|
|
|
subproblem_solver.set_model(self._lsape_model) |
|
|
|
|
|
subproblem_solver.solve() |
|
|
|
|
|
|
|
|
|
|
|
# Update and return overall substitution cost. |
|
|
|
|
|
cost += subproblem_solver.minimal_cost() |
|
|
|
|
|
return cost |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _compute_deletion_cost(self, g, v): |
|
|
|
|
|
# Collect node deletion cost. |
|
|
|
|
|
cost = self._ged_data.node_cost(g.nodes[v]['label'], SpecialLabel.DUMMY) |
|
|
|
|
|
|
|
|
|
|
|
# Collect edge deletion costs. |
|
|
|
|
|
for label in g[v].values(): |
|
|
|
|
|
cost += self._ged_data.edge_cost(label['label'], SpecialLabel.DUMMY) |
|
|
|
|
|
|
|
|
|
|
|
# Return overall deletion cost. |
|
|
|
|
|
return cost |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _compute_insertion_cost(self, g, v): |
|
|
|
|
|
# Collect node insertion cost. |
|
|
|
|
|
cost = self._ged_data.node_cost(SpecialLabel.DUMMY, g.nodes[v]['label']) |
|
|
|
|
|
|
|
|
|
|
|
# Collect edge insertion costs. |
|
|
|
|
|
for label in g[v].values(): |
|
|
|
|
|
cost += self._ged_data.edge_cost(SpecialLabel.DUMMY, label['label']) |
|
|
|
|
|
|
|
|
|
|
|
# Return overall insertion cost. |
|
|
|
|
|
return cost |