- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- Created on Wed Oct 16 14:20:06 2019
-
- @author: ljia
- """
- import numpy as np
- from tqdm import tqdm
- from itertools import combinations_with_replacement, combinations
- import multiprocessing
- from multiprocessing import Pool
- from functools import partial
- import time
- import random
-
- from scipy import optimize
- import cvxpy as cp
-
- import sys
- #sys.path.insert(0, "../")
- from ged import GED, get_nb_edit_operations
- from utils import kernel_distance_matrix
-
- def fit_GED_to_kernel_distance(Gn, node_label, edge_label, gkernel, itr_max, k=4,
- params_ged={'lib': 'gedlibpy', 'cost': 'CONSTANT',
- 'method': 'IPFP', 'stabilizer': None},
- init_costs=[3, 3, 1, 3, 3, 1],
- parallel=True):
- # c_vi, c_vr, c_vs, c_ei, c_er, c_es or parts of them.
- # random.seed(1)
- # cost_rdm = random.sample(range(1, 10), 6)
- # init_costs = cost_rdm + [0]
- # init_costs = cost_rdm
- init_costs = [3, 3, 1, 3, 3, 1]
- # init_costs = [i * 0.01 for i in cost_rdm] + [0]
- # init_costs = [0.2, 0.2, 0.2, 0.2, 0.2, 0]
- # init_costs = [0, 0, 0.9544, 0.026, 0.0196, 0]
- # init_costs = [0.008429912251810438, 0.025461055985319694, 0.2047320869225948, 0.004148727085832133, 0.0, 0]
- # idx_cost_nonzeros = [i for i, item in enumerate(edit_costs) if item != 0]
-
- # compute distances in feature space.
- dis_k_mat, _, _, _ = kernel_distance_matrix(Gn, node_label, edge_label, gkernel=gkernel)
- dis_k_vec = []
- for i in range(len(dis_k_mat)):
- # for j in range(i, len(dis_k_mat)):
- for j in range(i + 1, len(dis_k_mat)):
- dis_k_vec.append(dis_k_mat[i, j])
- dis_k_vec = np.array(dis_k_vec)
-
- # init ged.
- print('\ninitial:')
- time0 = time.time()
- params_ged['edit_cost_constant'] = init_costs
- ged_vec_init, ged_mat, n_edit_operations = compute_geds(Gn, params_ged,
- parallel=parallel)
- residual_list = [np.sqrt(np.sum(np.square(np.array(ged_vec_init) - dis_k_vec)))]
- time_list = [time.time() - time0]
- edit_cost_list = [init_costs]
- nb_cost_mat = np.array(n_edit_operations)
- nb_cost_mat_list = [nb_cost_mat]
- print('edit_costs:', init_costs)
- print('residual_list:', residual_list)
-
- for itr in range(itr_max):
- print('\niteration', itr)
- time0 = time.time()
- # "fit" geds to distances in feature space by tuning edit costs using the
- # Least Squares Method.
- edit_costs_new, residual = update_costs(nb_cost_mat, dis_k_vec)
- for i in range(len(edit_costs_new)):
- if edit_costs_new[i] < 0:
- if edit_costs_new[i] > -1e-9:
- edit_costs_new[i] = 0
- else:
- raise ValueError('The edit cost is negative.')
- # for i in range(len(edit_costs_new)):
- # if edit_costs_new[i] < 0:
- # edit_costs_new[i] = 0
-
- # compute new GEDs and numbers of edit operations.
- params_ged['edit_cost_constant'] = edit_costs_new
- ged_vec, ged_mat, n_edit_operations = compute_geds(Gn, params_ged,
- parallel=parallel)
- residual_list.append(np.sqrt(np.sum(np.square(np.array(ged_vec) - dis_k_vec))))
- time_list.append(time.time() - time0)
- edit_cost_list.append(edit_costs_new)
- nb_cost_mat = np.array(n_edit_operations)
- nb_cost_mat_list.append(nb_cost_mat)
- print('edit_costs:', edit_costs_new)
- print('residual_list:', residual_list)
-
- return edit_costs_new, residual_list, edit_cost_list, dis_k_mat, ged_mat, \
- time_list, nb_cost_mat_list
-
-
- def compute_geds(Gn, params_ged, parallel=False):
- ged_mat = np.zeros((len(Gn), len(Gn)))
- if parallel:
- # print('parallel')
- # len_itr = int(len(Gn) * (len(Gn) + 1) / 2)
- len_itr = int(len(Gn) * (len(Gn) - 1) / 2)
- ged_vec = [0 for i in range(len_itr)]
- n_edit_operations = [0 for i in range(len_itr)]
- # itr = combinations_with_replacement(range(0, len(Gn)), 2)
- itr = combinations(range(0, len(Gn)), 2)
- n_jobs = multiprocessing.cpu_count()
- if len_itr < 100 * n_jobs:
- chunksize = int(len_itr / n_jobs) + 1
- else:
- chunksize = 100
- def init_worker(gn_toshare):
- global G_gn
- G_gn = gn_toshare
- do_partial = partial(_wrapper_compute_ged_parallel, params_ged)
- pool = Pool(processes=n_jobs, initializer=init_worker, initargs=(Gn,))
- iterator = tqdm(pool.imap_unordered(do_partial, itr, chunksize),
- desc='computing GEDs', file=sys.stdout)
- # iterator = pool.imap_unordered(do_partial, itr, chunksize)
- for i, j, dis, n_eo_tmp in iterator:
- idx_itr = int(len(Gn) * i + j - (i + 1) * (i + 2) / 2)
- ged_vec[idx_itr] = dis
- ged_mat[i][j] = dis
- ged_mat[j][i] = dis
- n_edit_operations[idx_itr] = n_eo_tmp
- # print('\n-------------------------------------------')
- # print(i, j, idx_itr, dis)
- pool.close()
- pool.join()
-
- else:
- ged_vec = []
- n_edit_operations = []
- for i in tqdm(range(len(Gn)), desc='computing GEDs', file=sys.stdout):
- # for i in range(len(Gn)):
- for j in range(i + 1, len(Gn)):
- dis, pi_forward, pi_backward = GED(Gn[i], Gn[j], **params_ged)
- ged_vec.append(dis)
- ged_mat[i][j] = dis
- ged_mat[j][i] = dis
- n_eo_tmp = get_nb_edit_operations(Gn[i], Gn[j], pi_forward, pi_backward)
- n_edit_operations.append(n_eo_tmp)
-
- return ged_vec, ged_mat, n_edit_operations
-
-
- def _wrapper_compute_ged_parallel(params_ged, itr):
- i = itr[0]
- j = itr[1]
- dis, n_eo_tmp = _compute_ged_parallel(G_gn[i], G_gn[j], params_ged)
- return i, j, dis, n_eo_tmp
-
-
- def _compute_ged_parallel(g1, g2, params_ged):
- dis, pi_forward, pi_backward = GED(g1, g2, **params_ged)
- n_eo_tmp = get_nb_edit_operations(g1, g2, pi_forward, pi_backward)
- return dis, n_eo_tmp
-
-
- def update_costs(nb_cost_mat, dis_k_vec):
- # # method 1: simple least square method.
- # edit_costs_new, residual, _, _ = np.linalg.lstsq(nb_cost_mat, dis_k_vec,
- # rcond=None)
-
- # # method 2: least square method with x_i >= 0.
- # edit_costs_new, residual = optimize.nnls(nb_cost_mat, dis_k_vec)
-
- # method 3: solve as a quadratic program with constraints.
- # P = np.dot(nb_cost_mat.T, nb_cost_mat)
- # q_T = -2 * np.dot(dis_k_vec.T, nb_cost_mat)
- # G = -1 * np.identity(nb_cost_mat.shape[1])
- # h = np.array([0 for i in range(nb_cost_mat.shape[1])])
- # A = np.array([1 for i in range(nb_cost_mat.shape[1])])
- # b = 1
- # x = cp.Variable(nb_cost_mat.shape[1])
- # prob = cp.Problem(cp.Minimize(cp.quad_form(x, P) + q_T@x),
- # [G@x <= h])
- # prob.solve()
- # edit_costs_new = x.value
- # residual = prob.value - np.dot(dis_k_vec.T, dis_k_vec)
-
- # G = -1 * np.identity(nb_cost_mat.shape[1])
- # h = np.array([0 for i in range(nb_cost_mat.shape[1])])
- x = cp.Variable(nb_cost_mat.shape[1])
- cost = cp.sum_squares(nb_cost_mat * x - dis_k_vec)
- constraints = [x >= [0.0001 for i in range(nb_cost_mat.shape[1])],
- # np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
- np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
- np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
- prob = cp.Problem(cp.Minimize(cost), constraints)
- prob.solve()
- edit_costs_new = x.value
- residual = np.sqrt(prob.value)
-
- # method 4:
-
- return edit_costs_new, residual
-
-
- if __name__ == '__main__':
- print('check test_fitDistance.py')
|