@@ -1,9 +1,5 @@ | |||||
environment: | environment: | ||||
matrix: | matrix: | ||||
- PYTHON: "C:\\Python33" | |||||
- PYTHON: "C:\\Python33-x64" | |||||
- PYTHON: "C:\\Python34" | |||||
- PYTHON: "C:\\Python34-x64" | |||||
- PYTHON: "C:\\Python35" | - PYTHON: "C:\\Python35" | ||||
- PYTHON: "C:\\Python35-x64" | - PYTHON: "C:\\Python35-x64" | ||||
- PYTHON: "C:\\Python36" | - PYTHON: "C:\\Python36" | ||||
@@ -1 +1,2 @@ | |||||
from gklearn.ged.env.common_types import AlgorithmState | |||||
from gklearn.ged.env.common_types import AlgorithmState | |||||
from gklearn.ged.env.node_map import NodeMap |
@@ -0,0 +1,68 @@ | |||||
#!/usr/bin/env python3 | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Created on Wed Apr 22 11:31:26 2020 | |||||
@author: ljia | |||||
""" | |||||
import numpy as np | |||||
class NodeMap(object): | |||||
def __init__(self, num_nodes_g, num_nodes_h): | |||||
self.__forward_map = [np.inf] * num_nodes_g | |||||
self.__backward_map = [np.inf] * num_nodes_h | |||||
self.__induced_cost = np.inf | |||||
def num_source_nodes(self): | |||||
return len(self.__forward_map) | |||||
def num_target_nodes(self): | |||||
return len(self.__backward_map) | |||||
def image(self, node): | |||||
if node < len(self.__forward_map): | |||||
return self.__forward_map[node] | |||||
else: | |||||
raise Exception('The node with ID ', str(node), ' is not contained in the source nodes of the node map.') | |||||
return np.inf | |||||
def pre_image(self, node): | |||||
if node < len(self.__backward_map): | |||||
return self.__backward_map[node] | |||||
else: | |||||
raise Exception('The node with ID ', str(node), ' is not contained in the target nodes of the node map.') | |||||
return np.inf | |||||
def get_forward_map(self): | |||||
return self.__forward_map | |||||
def get_backward_map(self): | |||||
return self.__backward_map | |||||
def add_assignment(self, i, k): | |||||
if i != np.inf: | |||||
if i < len(self.__forward_map): | |||||
self.__forward_map[i] = k | |||||
else: | |||||
raise Exception('The node with ID ', str(i), ' is not contained in the source nodes of the node map.') | |||||
if k != np.inf: | |||||
if k < len(self.__backward_map): | |||||
self.__backward_map[k] = i | |||||
else: | |||||
raise Exception('The node with ID ', str(k), ' is not contained in the target nodes of the node map.') | |||||
def set_induced_cost(self, induced_cost): | |||||
self.__induced_cost = induced_cost | |||||
def induced_cost(self): | |||||
return self.__induced_cost |
@@ -47,6 +47,7 @@ class MedianGraphEstimator(object): | |||||
self.__desired_num_random_inits = 10 | self.__desired_num_random_inits = 10 | ||||
self.__use_real_randomness = True | self.__use_real_randomness = True | ||||
self.__seed = 0 | self.__seed = 0 | ||||
self.__update_order = True | |||||
self.__refine = True | self.__refine = True | ||||
self.__time_limit_in_sec = 0 | self.__time_limit_in_sec = 0 | ||||
self.__epsilon = 0.0001 | self.__epsilon = 0.0001 | ||||
@@ -126,6 +127,16 @@ class MedianGraphEstimator(object): | |||||
else: | else: | ||||
raise Exception('Invalid argument "' + opt_val + '" for option stdout. Usage: options = "[--stdout 0|1|2] [...]"') | raise Exception('Invalid argument "' + opt_val + '" for option stdout. Usage: options = "[--stdout 0|1|2] [...]"') | ||||
elif opt_name == 'update-order': | |||||
if opt_val == 'TRUE': | |||||
self.__update_order = True | |||||
elif opt_val == 'FALSE': | |||||
self.__update_order = False | |||||
else: | |||||
raise Exception('Invalid argument "' + opt_val + '" for option update-order. Usage: options = "[--update-order TRUE|FALSE] [...]"') | |||||
elif opt_name == 'refine': | elif opt_name == 'refine': | ||||
if opt_val == 'TRUE': | if opt_val == 'TRUE': | ||||
self.__refine = True | self.__refine = True | ||||
@@ -298,11 +309,11 @@ class MedianGraphEstimator(object): | |||||
for graph_id in graph_ids: | for graph_id in graph_ids: | ||||
# @todo: get_nx_graph() function may need to be modified according to the coming code. | # @todo: get_nx_graph() function may need to be modified according to the coming code. | ||||
graphs[graph_id] = self.__ged_env.get_nx_graph(graph_id, True, True, False) | graphs[graph_id] = self.__ged_env.get_nx_graph(graph_id, True, True, False) | ||||
# print(self.__ged_env.get_graph_internal_id(0)) | |||||
# print(graphs[0].graph) | |||||
# print(graphs[0].nodes(data=True)) | |||||
# print(graphs[0].edges(data=True)) | |||||
# print(nx.adjacency_matrix(graphs[0])) | |||||
# print(self.__ged_env.get_graph_internal_id(0)) | |||||
# print(graphs[0].graph) | |||||
# print(graphs[0].nodes(data=True)) | |||||
# print(graphs[0].edges(data=True)) | |||||
# print(nx.adjacency_matrix(graphs[0])) | |||||
# Construct initial medians. | # Construct initial medians. | ||||
@@ -310,10 +321,10 @@ class MedianGraphEstimator(object): | |||||
self.__construct_initial_medians(graph_ids, timer, medians) | self.__construct_initial_medians(graph_ids, timer, medians) | ||||
end_init = time.time() | end_init = time.time() | ||||
self.__runtime_initialized = end_init - start | self.__runtime_initialized = end_init - start | ||||
# print(medians[0].graph) | |||||
# print(medians[0].nodes(data=True)) | |||||
# print(medians[0].edges(data=True)) | |||||
# print(nx.adjacency_matrix(medians[0])) | |||||
# print(medians[0].graph) | |||||
# print(medians[0].nodes(data=True)) | |||||
# print(medians[0].edges(data=True)) | |||||
# print(nx.adjacency_matrix(medians[0])) | |||||
# Reset information about iterations and number of times the median decreases and increases. | # Reset information about iterations and number of times the median decreases and increases. | ||||
self.__itrs = [0] * len(medians) | self.__itrs = [0] * len(medians) | ||||
@@ -353,12 +364,12 @@ class MedianGraphEstimator(object): | |||||
# Compute node maps and sum of distances for initial median. | # Compute node maps and sum of distances for initial median. | ||||
self.__sum_of_distances = 0 | self.__sum_of_distances = 0 | ||||
self.__node_maps_from_median.clear() # @todo | |||||
self.__node_maps_from_median.clear() | |||||
for graph_id in graph_ids: | for graph_id in graph_ids: | ||||
self.__ged_env.run_method(gen_median_id, graph_id) | self.__ged_env.run_method(gen_median_id, graph_id) | ||||
self.__node_maps_from_median[graph_id] = self.__ged_env.get_node_map(gen_median_id, graph_id) | self.__node_maps_from_median[graph_id] = self.__ged_env.get_node_map(gen_median_id, graph_id) | ||||
# print(self.__node_maps_from_median[graph_id]) | # print(self.__node_maps_from_median[graph_id]) | ||||
self.__sum_of_distances += self.__ged_env.get_induced_cost(gen_median_id, graph_id) # @todo: the C++ implementation for this function in GedLibBind.ipp re-call get_node_map() once more, this is not neccessary. | |||||
self.__sum_of_distances += self.__node_maps_from_median[graph_id].induced_cost() | |||||
# print(self.__sum_of_distances) | # print(self.__sum_of_distances) | ||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -366,7 +377,7 @@ class MedianGraphEstimator(object): | |||||
self.__best_init_sum_of_distances = min(self.__best_init_sum_of_distances, self.__sum_of_distances) | self.__best_init_sum_of_distances = min(self.__best_init_sum_of_distances, self.__sum_of_distances) | ||||
self.__ged_env.load_nx_graph(median, set_median_id) | self.__ged_env.load_nx_graph(median, set_median_id) | ||||
# print(self.__best_init_sum_of_distances) | |||||
print(self.__best_init_sum_of_distances) | |||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -391,10 +402,11 @@ class MedianGraphEstimator(object): | |||||
# Update the median. # @todo!!!!!!!!!!!!!!!!!!!!!! | # Update the median. # @todo!!!!!!!!!!!!!!!!!!!!!! | ||||
median_modified = self.__update_median(graphs, median) | median_modified = self.__update_median(graphs, median) | ||||
if not median_modified or self.__itrs[median_pos] == 0: | |||||
decreased_order = self.__decrease_order(graphs, median) | |||||
if not decreased_order or self.__itrs[median_pos] == 0: | |||||
increased_order = False | |||||
if self.__update_order: | |||||
if not median_modified or self.__itrs[median_pos] == 0: | |||||
decreased_order = self.__decrease_order(graphs, median) | |||||
if not decreased_order or self.__itrs[median_pos] == 0: | |||||
increased_order = False | |||||
# Update the number of iterations without update of the median. | # Update the number of iterations without update of the median. | ||||
if median_modified or decreased_order or increased_order: | if median_modified or decreased_order or increased_order: | ||||
@@ -421,11 +433,11 @@ class MedianGraphEstimator(object): | |||||
# Compute induced costs of the old node maps w.r.t. the updated median. | # Compute induced costs of the old node maps w.r.t. the updated median. | ||||
for graph_id in graph_ids: | for graph_id in graph_ids: | ||||
# print(self.__ged_env.get_induced_cost(gen_median_id, graph_id)) | |||||
# @todo: watch out if compute_induced_cost is correct, this may influence: increase/decrease order, induced_cost() in the following code.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |||||
self.__ged_env.compute_induced_cost(gen_median_id, graph_id) | |||||
# print('---------------------------------------') | |||||
# print(self.__ged_env.get_induced_cost(gen_median_id, graph_id)) | |||||
# print(self.__node_maps_from_median[graph_id].induced_cost()) | |||||
self.__ged_env.compute_induced_cost(gen_median_id, graph_id, self.__node_maps_from_median[graph_id]) | |||||
# print('---------------------------------------') | |||||
# print(self.__node_maps_from_median[graph_id].induced_cost()) | |||||
# @todo:!!!!!!!!!!!!!!!!!!!!!!!!!!!!This value is a slight different from the c++ program, which might be a bug! Use it very carefully! | |||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -439,8 +451,9 @@ class MedianGraphEstimator(object): | |||||
# Update the sum of distances. | # Update the sum of distances. | ||||
old_sum_of_distances = self.__sum_of_distances | old_sum_of_distances = self.__sum_of_distances | ||||
self.__sum_of_distances = 0 | self.__sum_of_distances = 0 | ||||
for graph_id in self.__node_maps_from_median: | |||||
self.__sum_of_distances += self.__ged_env.get_induced_cost(gen_median_id, graph_id) # @todo: see above. | |||||
for graph_id, node_map in self.__node_maps_from_median.items(): | |||||
self.__sum_of_distances += node_map.induced_cost() | |||||
# print(self.__sum_of_distances) | |||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -460,7 +473,7 @@ class MedianGraphEstimator(object): | |||||
# Update the best median. | # Update the best median. | ||||
if self.__sum_of_distances < best_sum_of_distances: | if self.__sum_of_distances < best_sum_of_distances: | ||||
best_sum_of_distances = self.__sum_of_distances | best_sum_of_distances = self.__sum_of_distances | ||||
node_maps_from_best_median = self.__node_maps_from_median | |||||
node_maps_from_best_median = self.__node_maps_from_median.copy() # @todo: this is a shallow copy, not sure if it is enough. | |||||
best_median = median | best_median = median | ||||
# Update the number of converged descents. | # Update the number of converged descents. | ||||
@@ -543,6 +556,7 @@ class MedianGraphEstimator(object): | |||||
self.__desired_num_random_inits = 10 | self.__desired_num_random_inits = 10 | ||||
self.__use_real_randomness = True | self.__use_real_randomness = True | ||||
self.__seed = 0 | self.__seed = 0 | ||||
self.__update_order = True | |||||
self.__refine = True | self.__refine = True | ||||
self.__time_limit_in_sec = 0 | self.__time_limit_in_sec = 0 | ||||
self.__epsilon = 0.0001 | self.__epsilon = 0.0001 | ||||
@@ -568,16 +582,16 @@ class MedianGraphEstimator(object): | |||||
self.__compute_medoid(graph_ids, timer, initial_medians) | self.__compute_medoid(graph_ids, timer, initial_medians) | ||||
elif self.__init_type == 'MAX': | elif self.__init_type == 'MAX': | ||||
pass # @todo | pass # @todo | ||||
# compute_max_order_graph_(graph_ids, initial_medians) | |||||
# compute_max_order_graph_(graph_ids, initial_medians) | |||||
elif self.__init_type == 'MIN': | elif self.__init_type == 'MIN': | ||||
pass # @todo | pass # @todo | ||||
# compute_min_order_graph_(graph_ids, initial_medians) | |||||
# compute_min_order_graph_(graph_ids, initial_medians) | |||||
elif self.__init_type == 'MEAN': | elif self.__init_type == 'MEAN': | ||||
pass # @todo | pass # @todo | ||||
# compute_mean_order_graph_(graph_ids, initial_medians) | |||||
# compute_mean_order_graph_(graph_ids, initial_medians) | |||||
else: | else: | ||||
pass # @todo | pass # @todo | ||||
# sample_initial_medians_(graph_ids, initial_medians) | |||||
# sample_initial_medians_(graph_ids, initial_medians) | |||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -655,20 +669,20 @@ class MedianGraphEstimator(object): | |||||
# Iterate through all nodes of the median. | # Iterate through all nodes of the median. | ||||
for i in range(0, nx.number_of_nodes(median)): | for i in range(0, nx.number_of_nodes(median)): | ||||
# print('i: ', i) | |||||
# print('i: ', i) | |||||
# Collect the labels of the substituted nodes. | # Collect the labels of the substituted nodes. | ||||
node_labels = [] | node_labels = [] | ||||
for graph_id, graph in graphs.items(): | for graph_id, graph in graphs.items(): | ||||
# print('graph_id: ', graph_id) | |||||
# print(self.__node_maps_from_median[graph_id]) | |||||
k = self.__get_node_image_from_map(self.__node_maps_from_median[graph_id], i) | |||||
# print('k: ', k) | |||||
# print('graph_id: ', graph_id) | |||||
# print(self.__node_maps_from_median[graph_id]) | |||||
k = self.__node_maps_from_median[graph_id].image(i) | |||||
# print('k: ', k) | |||||
if k != np.inf: | if k != np.inf: | ||||
node_labels.append(graph.nodes[k]) | node_labels.append(graph.nodes[k]) | ||||
# Compute the median label and update the median. | # Compute the median label and update the median. | ||||
if len(node_labels) > 0: | if len(node_labels) > 0: | ||||
# median_label = self.__ged_env.get_median_node_label(node_labels) | |||||
# median_label = self.__ged_env.get_median_node_label(node_labels) | |||||
median_label = self.__get_median_node_label(node_labels) | median_label = self.__get_median_node_label(node_labels) | ||||
if self.__ged_env.get_node_rel_cost(median.nodes[i], median_label) > self.__epsilon: | if self.__ged_env.get_node_rel_cost(median.nodes[i], median_label) > self.__epsilon: | ||||
nx.set_node_attributes(median, {i: median_label}) | nx.set_node_attributes(median, {i: median_label}) | ||||
@@ -679,10 +693,10 @@ class MedianGraphEstimator(object): | |||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
print('edges ... ', end='') | print('edges ... ', end='') | ||||
# Clear the adjacency lists of the median and reset number of edges to 0. | |||||
median_edges = list(median.edges) | |||||
for (head, tail) in median_edges: | |||||
median.remove_edge(head, tail) | |||||
# # Clear the adjacency lists of the median and reset number of edges to 0. | |||||
# median_edges = list(median.edges) | |||||
# for (head, tail) in median_edges: | |||||
# median.remove_edge(head, tail) | |||||
# @todo: what if edge is not labeled? | # @todo: what if edge is not labeled? | ||||
# Iterate through all possible edges (i,j) of the median. | # Iterate through all possible edges (i,j) of the median. | ||||
@@ -692,8 +706,8 @@ class MedianGraphEstimator(object): | |||||
# Collect the labels of the edges to which (i,j) is mapped by the node maps. | # Collect the labels of the edges to which (i,j) is mapped by the node maps. | ||||
edge_labels = [] | edge_labels = [] | ||||
for graph_id, graph in graphs.items(): | for graph_id, graph in graphs.items(): | ||||
k = self.__get_node_image_from_map(self.__node_maps_from_median[graph_id], i) | |||||
l = self.__get_node_image_from_map(self.__node_maps_from_median[graph_id], j) | |||||
k = self.__node_maps_from_median[graph_id].image(i) | |||||
l = self.__node_maps_from_median[graph_id].image(j) | |||||
if k != np.inf and l != np.inf: | if k != np.inf and l != np.inf: | ||||
if graph.has_edge(k, l): | if graph.has_edge(k, l): | ||||
edge_labels.append(graph.edges[(k, l)]) | edge_labels.append(graph.edges[(k, l)]) | ||||
@@ -711,11 +725,13 @@ class MedianGraphEstimator(object): | |||||
rel_cost += self.__ged_env.get_edge_rel_cost(median_label, edge_label) | rel_cost += self.__ged_env.get_edge_rel_cost(median_label, edge_label) | ||||
# Update the median. | # Update the median. | ||||
if median.has_edge(i, j): | |||||
median.remove_edge(i, j) | |||||
if rel_cost < (self.__edge_ins_cost + self.__edge_del_cost) * len(edge_labels) - self.__edge_del_cost * len(graphs): | if rel_cost < (self.__edge_ins_cost + self.__edge_del_cost) * len(edge_labels) - self.__edge_del_cost * len(graphs): | ||||
median.add_edge(i, j, **median_label) | median.add_edge(i, j, **median_label) | ||||
else: | |||||
if median.has_edge(i, j): | |||||
median.remove_edge(i, j) | |||||
# else: | |||||
# if median.has_edge(i, j): | |||||
# median.remove_edge(i, j) | |||||
def __update_node_maps(self): | def __update_node_maps(self): | ||||
@@ -725,10 +741,12 @@ class MedianGraphEstimator(object): | |||||
# Update the node maps. | # Update the node maps. | ||||
node_maps_were_modified = False | node_maps_were_modified = False | ||||
for graph_id in self.__node_maps_from_median: | |||||
for graph_id, node_map in self.__node_maps_from_median.items(): | |||||
self.__ged_env.run_method(self.__median_id, graph_id) | self.__ged_env.run_method(self.__median_id, graph_id) | ||||
if self.__ged_env.get_upper_bound(self.__median_id, graph_id) < self.__ged_env.get_induced_cost(self.__median_id, graph_id) - self.__epsilon: # @todo: see above. | |||||
self.__node_maps_from_median[graph_id] = self.__ged_env.get_node_map(self.__median_id, graph_id) # @todo: node_map may not assigned. | |||||
if self.__ged_env.get_upper_bound(self.__median_id, graph_id) < node_map.induced_cost() - self.__epsilon: | |||||
# xxx = self.__node_maps_from_median[graph_id] | |||||
self.__node_maps_from_median[graph_id] = self.__ged_env.get_node_map(self.__median_id, graph_id) | |||||
# yyy = self.__node_maps_from_median[graph_id] | |||||
node_maps_were_modified = True | node_maps_were_modified = True | ||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -748,13 +766,13 @@ class MedianGraphEstimator(object): | |||||
print('Trying to decrease order: ... ', end='') | print('Trying to decrease order: ... ', end='') | ||||
# Initialize ID of the node that is to be deleted. | # Initialize ID of the node that is to be deleted. | ||||
id_deleted_node = None # @todo: or np.inf | |||||
id_deleted_node = [None] # @todo: or np.inf | |||||
decreased_order = False | decreased_order = False | ||||
# Decrease the order as long as the best deletion delta is negative. | # Decrease the order as long as the best deletion delta is negative. | ||||
while self.__compute_best_deletion_delta(graphs, median, [id_deleted_node]) < -self.__epsilon: | |||||
while self.__compute_best_deletion_delta(graphs, median, id_deleted_node) < -self.__epsilon: # @todo | |||||
decreased_order = True | decreased_order = True | ||||
self.__delete_node_from_median(id_deleted_node, median) | |||||
self.__delete_node_from_median(id_deleted_node[0], median) | |||||
# Print information about current iteration. | # Print information about current iteration. | ||||
if self.__print_to_stdout == 2: | if self.__print_to_stdout == 2: | ||||
@@ -777,7 +795,7 @@ class MedianGraphEstimator(object): | |||||
delta -= self.__node_del_cost | delta -= self.__node_del_cost | ||||
else: | else: | ||||
delta += self.__node_ins_cost - self.__ged_env.get_node_rel_cost(median.nodes[i], graph.nodes[k]) | delta += self.__node_ins_cost - self.__ged_env.get_node_rel_cost(median.nodes[i], graph.nodes[k]) | ||||
for j, j_label in median[i]: | |||||
for j, j_label in median[i].items(): | |||||
l = self.__get_node_image_from_map(self.__node_maps_from_median[graph_id], j) | l = self.__get_node_image_from_map(self.__node_maps_from_median[graph_id], j) | ||||
if k == np.inf or l == np.inf: | if k == np.inf or l == np.inf: | ||||
delta -= self.__edge_del_cost | delta -= self.__edge_del_cost | ||||
@@ -790,32 +808,34 @@ class MedianGraphEstimator(object): | |||||
if delta < best_delta - self.__epsilon: | if delta < best_delta - self.__epsilon: | ||||
best_delta = delta | best_delta = delta | ||||
id_deleted_node[0] = i | id_deleted_node[0] = i | ||||
id_deleted_node[0] = i # @todo: | |||||
return best_delta | return best_delta | ||||
def __delete_node_from_median(self, id_deleted_node, median): | |||||
# Update the nodes of the median. | |||||
median.remove_node(id_deleted_node) # @todo: test if it is right. | |||||
def __delete_node_from_median(self, id_deleted_node, median): # @todo: update env.node_map? | |||||
# Update the median. | |||||
median.remove_node(id_deleted_node) | |||||
# Update the node maps. | # Update the node maps. | ||||
for _, node_map in self.__node_maps_from_median.items(): | for _, node_map in self.__node_maps_from_median.items(): | ||||
new_node_map = {nx.number_of_nodes(median): ''} # @todo | |||||
is_unassigned_target_node = ['', True] | |||||
new_node_map = [] # @todo | |||||
is_unassigned_target_node = [True] * len(node_map) | |||||
for i in range(0, nx.number_of_nodes(median)): | for i in range(0, nx.number_of_nodes(median)): | ||||
if i != id_deleted_node: | if i != id_deleted_node: | ||||
new_i = (i if i < id_deleted_node else i - 1) | new_i = (i if i < id_deleted_node else i - 1) | ||||
k = self.__get_node_image_from_map(node_map, i) | k = self.__get_node_image_from_map(node_map, i) | ||||
new_node_map["ds"] # @todo | |||||
new_node_map.append((new_i, k)) # @todo | |||||
if k != np.inf: | if k != np.inf: | ||||
is_unassigned_target_node[k] = False | is_unassigned_target_node[k] = False | ||||
for k in range(0, ''): | |||||
for k in range(0, len(node_map)): | |||||
if is_unassigned_target_node[k]: | if is_unassigned_target_node[k]: | ||||
new_node_map.sdf[] | |||||
node_map = new_node_map | |||||
new_node_map.append(np.inf, k) | |||||
node_map = new_node_map # @todo | |||||
# Increase overall number of decreases. | # Increase overall number of decreases. | ||||
self.__num_decrease_order += 1 | self.__num_decrease_order += 1 | ||||
def __improve_sum_of_distances(self, timer): | def __improve_sum_of_distances(self, timer): | ||||
pass | pass | ||||
@@ -825,37 +845,37 @@ class MedianGraphEstimator(object): | |||||
return self.__median_id != np.inf | return self.__median_id != np.inf | ||||
def __get_node_image_from_map(self, node_map, node): | |||||
""" | |||||
Return ID of the node mapping of `node` in `node_map`. | |||||
# def __get_node_image_from_map(self, node_map, node): | |||||
# """ | |||||
# Return ID of the node mapping of `node` in `node_map`. | |||||
Parameters | |||||
---------- | |||||
node_map : list[tuple(int, int)] | |||||
List of node maps where the mapping node is found. | |||||
node : int | |||||
The mapping node of this node is returned | |||||
# Parameters | |||||
# ---------- | |||||
# node_map : list[tuple(int, int)] | |||||
# List of node maps where the mapping node is found. | |||||
# | |||||
# node : int | |||||
# The mapping node of this node is returned | |||||
Raises | |||||
------ | |||||
Exception | |||||
If the node with ID `node` is not contained in the source nodes of the node map. | |||||
# Raises | |||||
# ------ | |||||
# Exception | |||||
# If the node with ID `node` is not contained in the source nodes of the node map. | |||||
Returns | |||||
------- | |||||
int | |||||
ID of the mapping of `node`. | |||||
Notes | |||||
----- | |||||
This function is not implemented in the `ged::MedianGraphEstimator` class of the `GEDLIB` library. Instead it is a Python implementation of the `ged::NodeMap::image` function. | |||||
""" | |||||
if node < len(node_map): | |||||
return node_map[node][1] if node_map[node][1] < len(node_map) else np.inf | |||||
else: | |||||
raise Exception('The node with ID ', str(node), ' is not contained in the source nodes of the node map.') | |||||
return np.inf | |||||
# Returns | |||||
# ------- | |||||
# int | |||||
# ID of the mapping of `node`. | |||||
# | |||||
# Notes | |||||
# ----- | |||||
# This function is not implemented in the `ged::MedianGraphEstimator` class of the `GEDLIB` library. Instead it is a Python implementation of the `ged::NodeMap::image` function. | |||||
# """ | |||||
# if node < len(node_map): | |||||
# return node_map[node][1] if node_map[node][1] < len(node_map) else np.inf | |||||
# else: | |||||
# raise Exception('The node with ID ', str(node), ' is not contained in the source nodes of the node map.') | |||||
# return np.inf | |||||
def __are_graphs_equal(self, g1, g2): | def __are_graphs_equal(self, g1, g2): | ||||
@@ -958,9 +978,9 @@ class MedianGraphEstimator(object): | |||||
for label in labels: | for label in labels: | ||||
coords = {} | coords = {} | ||||
for key, val in label.items(): | for key, val in label.items(): | ||||
label = float(val) | |||||
sums[key] += label | |||||
coords[key] = label | |||||
label_f = float(val) | |||||
sums[key] += label_f | |||||
coords[key] = label_f | |||||
labels_as_coords.append(coords) | labels_as_coords.append(coords) | ||||
median = {} | median = {} | ||||
for key, val in sums.items(): | for key, val in sums.items(): | ||||
@@ -980,7 +1000,7 @@ class MedianGraphEstimator(object): | |||||
norm = 0 | norm = 0 | ||||
for key, val in label_as_coord.items(): | for key, val in label_as_coord.items(): | ||||
norm += (val - median[key]) ** 2 | norm += (val - median[key]) ** 2 | ||||
norm += np.sqrt(norm) | |||||
norm = np.sqrt(norm) | |||||
if norm > 0: | if norm > 0: | ||||
for key, val in label_as_coord.items(): | for key, val in label_as_coord.items(): | ||||
numerator[key] += val / norm | numerator[key] += val / norm | ||||
@@ -1005,64 +1025,64 @@ class MedianGraphEstimator(object): | |||||
return median_label | return median_label | ||||
# def __get_median_edge_label_symbolic(self, edge_labels): | |||||
# pass | |||||
# def __get_median_edge_label_symbolic(self, edge_labels): | |||||
# pass | |||||
# def __get_median_edge_label_nonsymbolic(self, edge_labels): | |||||
# if len(edge_labels) == 0: | |||||
# return {} | |||||
# else: | |||||
# # Transform the labels into coordinates and compute mean label as initial solution. | |||||
# edge_labels_as_coords = [] | |||||
# sums = {} | |||||
# for key, val in edge_labels[0].items(): | |||||
# sums[key] = 0 | |||||
# for edge_label in edge_labels: | |||||
# coords = {} | |||||
# for key, val in edge_label.items(): | |||||
# label = float(val) | |||||
# sums[key] += label | |||||
# coords[key] = label | |||||
# edge_labels_as_coords.append(coords) | |||||
# median = {} | |||||
# for key, val in sums.items(): | |||||
# median[key] = val / len(edge_labels) | |||||
# | |||||
# # Run main loop of Weiszfeld's Algorithm. | |||||
# epsilon = 0.0001 | |||||
# delta = 1.0 | |||||
# num_itrs = 0 | |||||
# all_equal = False | |||||
# while ((delta > epsilon) and (num_itrs < 100) and (not all_equal)): | |||||
# numerator = {} | |||||
# for key, val in sums.items(): | |||||
# numerator[key] = 0 | |||||
# denominator = 0 | |||||
# for edge_label_as_coord in edge_labels_as_coords: | |||||
# norm = 0 | |||||
# for key, val in edge_label_as_coord.items(): | |||||
# norm += (val - median[key]) ** 2 | |||||
# norm += np.sqrt(norm) | |||||
# if norm > 0: | |||||
# for key, val in edge_label_as_coord.items(): | |||||
# numerator[key] += val / norm | |||||
# denominator += 1.0 / norm | |||||
# if denominator == 0: | |||||
# all_equal = True | |||||
# else: | |||||
# new_median = {} | |||||
# delta = 0.0 | |||||
# for key, val in numerator.items(): | |||||
# this_median = val / denominator | |||||
# new_median[key] = this_median | |||||
# delta += np.abs(median[key] - this_median) | |||||
# median = new_median | |||||
# | |||||
# num_itrs += 1 | |||||
# | |||||
# # Transform the solution to ged::GXLLabel and return it. | |||||
# median_label = {} | |||||
# for key, val in median.items(): | |||||
# median_label[key] = str(val) | |||||
# return median_label | |||||
# def __get_median_edge_label_nonsymbolic(self, edge_labels): | |||||
# if len(edge_labels) == 0: | |||||
# return {} | |||||
# else: | |||||
# # Transform the labels into coordinates and compute mean label as initial solution. | |||||
# edge_labels_as_coords = [] | |||||
# sums = {} | |||||
# for key, val in edge_labels[0].items(): | |||||
# sums[key] = 0 | |||||
# for edge_label in edge_labels: | |||||
# coords = {} | |||||
# for key, val in edge_label.items(): | |||||
# label = float(val) | |||||
# sums[key] += label | |||||
# coords[key] = label | |||||
# edge_labels_as_coords.append(coords) | |||||
# median = {} | |||||
# for key, val in sums.items(): | |||||
# median[key] = val / len(edge_labels) | |||||
# | |||||
# # Run main loop of Weiszfeld's Algorithm. | |||||
# epsilon = 0.0001 | |||||
# delta = 1.0 | |||||
# num_itrs = 0 | |||||
# all_equal = False | |||||
# while ((delta > epsilon) and (num_itrs < 100) and (not all_equal)): | |||||
# numerator = {} | |||||
# for key, val in sums.items(): | |||||
# numerator[key] = 0 | |||||
# denominator = 0 | |||||
# for edge_label_as_coord in edge_labels_as_coords: | |||||
# norm = 0 | |||||
# for key, val in edge_label_as_coord.items(): | |||||
# norm += (val - median[key]) ** 2 | |||||
# norm += np.sqrt(norm) | |||||
# if norm > 0: | |||||
# for key, val in edge_label_as_coord.items(): | |||||
# numerator[key] += val / norm | |||||
# denominator += 1.0 / norm | |||||
# if denominator == 0: | |||||
# all_equal = True | |||||
# else: | |||||
# new_median = {} | |||||
# delta = 0.0 | |||||
# for key, val in numerator.items(): | |||||
# this_median = val / denominator | |||||
# new_median[key] = this_median | |||||
# delta += np.abs(median[key] - this_median) | |||||
# median = new_median | |||||
# | |||||
# num_itrs += 1 | |||||
# | |||||
# # Transform the solution to ged::GXLLabel and return it. | |||||
# median_label = {} | |||||
# for key, val in median.items(): | |||||
# median_label[key] = str(val) | |||||
# return median_label |
@@ -7,11 +7,10 @@ Created on Mon Mar 16 17:26:40 2020 | |||||
""" | """ | ||||
def test_median_graph_estimator(): | def test_median_graph_estimator(): | ||||
from gklearn.utils.graphfiles import loadDataset | |||||
from gklearn.utils import load_dataset | |||||
from gklearn.ged.median import MedianGraphEstimator, constant_node_costs | from gklearn.ged.median import MedianGraphEstimator, constant_node_costs | ||||
from gklearn.gedlib import librariesImport, gedlibpy | from gklearn.gedlib import librariesImport, gedlibpy | ||||
from gklearn.preimage.utils import get_same_item_indices | from gklearn.preimage.utils import get_same_item_indices | ||||
from gklearn.preimage.ged import convertGraph | |||||
import multiprocessing | import multiprocessing | ||||
# estimator parameters. | # estimator parameters. | ||||
@@ -22,17 +21,20 @@ def test_median_graph_estimator(): | |||||
# algorithm parameters. | # algorithm parameters. | ||||
algo = 'IPFP' | algo = 'IPFP' | ||||
initial_solutions = 40 | |||||
algo_options_suffix = ' --initial-solutions ' + str(initial_solutions) + ' --ratio-runs-from-initial-solutions 1' | |||||
initial_solutions = 1 | |||||
algo_options_suffix = ' --initial-solutions ' + str(initial_solutions) + ' --ratio-runs-from-initial-solutions 1 --initialization-method NODE ' | |||||
edit_cost_name = 'LETTER2' | edit_cost_name = 'LETTER2' | ||||
# edit_cost_name = 'CONSTANT' | |||||
edit_cost_constants = [0.02987291, 0.0178211, 0.01431966, 0.001, 0.001] | edit_cost_constants = [0.02987291, 0.0178211, 0.01431966, 0.001, 0.001] | ||||
# edit_cost_constants = [4, 4, 2, 1, 1, 1] | |||||
ds_name = 'COIL-DEL' | ds_name = 'COIL-DEL' | ||||
# Load dataset. | # Load dataset. | ||||
# dataset = '../../datasets/COIL-DEL/COIL-DEL_A.txt' | # dataset = '../../datasets/COIL-DEL/COIL-DEL_A.txt' | ||||
dataset = '../../../datasets/Letter-high/Letter-high_A.txt' | dataset = '../../../datasets/Letter-high/Letter-high_A.txt' | ||||
Gn, y_all = loadDataset(dataset) | |||||
# dataset = '../../../datasets/MUTAG/MUTAG_A.txt' | |||||
Gn, y_all, _ = load_dataset(dataset) | |||||
y_idx = get_same_item_indices(y_all) | y_idx = get_same_item_indices(y_all) | ||||
for i, (y, values) in enumerate(y_idx.items()): | for i, (y, values) in enumerate(y_idx.items()): | ||||
Gn_i = [Gn[val] for val in values] | Gn_i = [Gn[val] for val in values] | ||||
@@ -43,7 +45,7 @@ def test_median_graph_estimator(): | |||||
# gedlibpy.restart_env() | # gedlibpy.restart_env() | ||||
ged_env.set_edit_cost(edit_cost_name, edit_cost_constant=edit_cost_constants) | ged_env.set_edit_cost(edit_cost_name, edit_cost_constant=edit_cost_constants) | ||||
for G in Gn_i: | for G in Gn_i: | ||||
ged_env.add_nx_graph(convertGraph(G, edit_cost_name), '') | |||||
ged_env.add_nx_graph(G, '') | |||||
graph_ids = ged_env.get_all_graph_ids() | graph_ids = ged_env.get_all_graph_ids() | ||||
set_median_id = ged_env.add_graph('set_median') | set_median_id = ged_env.add_graph('set_median') | ||||
gen_median_id = ged_env.add_graph('gen_median') | gen_median_id = ged_env.add_graph('gen_median') | ||||
@@ -54,11 +56,13 @@ def test_median_graph_estimator(): | |||||
mge.set_refine_method(algo, '--threads ' + str(threads) + ' --initial-solutions ' + str(initial_solutions) + ' --ratio-runs-from-initial-solutions 1') | mge.set_refine_method(algo, '--threads ' + str(threads) + ' --initial-solutions ' + str(initial_solutions) + ' --ratio-runs-from-initial-solutions 1') | ||||
mge_options = '--time-limit ' + str(time_limit) + ' --stdout 2 --init-type ' + init_type | mge_options = '--time-limit ' + str(time_limit) + ' --stdout 2 --init-type ' + init_type | ||||
mge_options += ' --random-inits ' + str(num_inits) + ' --seed ' + '1' + ' --refine FALSE'# @todo: std::to_string(rng()) | |||||
mge_options += ' --random-inits ' + str(num_inits) + ' --seed ' + '1' + ' --update-order FALSE --refine FALSE'# @todo: std::to_string(rng()) | |||||
# Select the GED algorithm. | # Select the GED algorithm. | ||||
algo_options = '--threads ' + str(threads) + algo_options_suffix | algo_options = '--threads ' + str(threads) + algo_options_suffix | ||||
mge.set_options(mge_options) | mge.set_options(mge_options) | ||||
mge.set_label_names(node_labels=[], edge_labels=[], | |||||
node_attrs=['x', 'y'], edge_attrs=[]) | |||||
mge.set_init_method(algo, algo_options) | mge.set_init_method(algo, algo_options) | ||||
mge.set_descent_method(algo, algo_options) | mge.set_descent_method(algo, algo_options) | ||||
@@ -30,6 +30,8 @@ def mge_options_to_string(options): | |||||
opt_str += '--randomness ' + str(val) + ' ' | opt_str += '--randomness ' + str(val) + ' ' | ||||
elif key == 'verbose': | elif key == 'verbose': | ||||
opt_str += '--stdout ' + str(val) + ' ' | opt_str += '--stdout ' + str(val) + ' ' | ||||
elif key == 'update_order': | |||||
opt_str += '--update-order ' + ('TRUE' if val else 'FALSE') + ' ' | |||||
elif key == 'refine': | elif key == 'refine': | ||||
opt_str += '--refine ' + ('TRUE' if val else 'FALSE') + ' ' | opt_str += '--refine ' + ('TRUE' if val else 'FALSE') + ' ' | ||||
elif key == 'time_limit': | elif key == 'time_limit': | ||||
@@ -35,8 +35,8 @@ from libcpp.pair cimport pair | |||||
from libcpp.list cimport list | from libcpp.list cimport list | ||||
#Long unsigned int equivalent | #Long unsigned int equivalent | ||||
cimport numpy as np | |||||
ctypedef np.npy_uint32 UINT32_t | |||||
cimport numpy as cnp | |||||
ctypedef cnp.npy_uint32 UINT32_t | |||||
from cpython cimport array | from cpython cimport array | ||||
@@ -76,14 +76,14 @@ cdef extern from "src/GedLibBind.hpp" namespace "pyged": | |||||
void runMethod(size_t g, size_t h) except + | void runMethod(size_t g, size_t h) except + | ||||
double getUpperBound(size_t g, size_t h) except + | double getUpperBound(size_t g, size_t h) except + | ||||
double getLowerBound(size_t g, size_t h) except + | double getLowerBound(size_t g, size_t h) except + | ||||
vector[np.npy_uint64] getForwardMap(size_t g, size_t h) except + | |||||
vector[np.npy_uint64] getBackwardMap(size_t g, size_t h) except + | |||||
vector[cnp.npy_uint64] getForwardMap(size_t g, size_t h) except + | |||||
vector[cnp.npy_uint64] getBackwardMap(size_t g, size_t h) except + | |||||
size_t getNodeImage(size_t g, size_t h, size_t nodeId) except + | size_t getNodeImage(size_t g, size_t h, size_t nodeId) except + | ||||
size_t getNodePreImage(size_t g, size_t h, size_t nodeId) except + | size_t getNodePreImage(size_t g, size_t h, size_t nodeId) except + | ||||
double getInducedCost(size_t g, size_t h) except + | double getInducedCost(size_t g, size_t h) except + | ||||
vector[pair[size_t,size_t]] getNodeMap(size_t g, size_t h) except + | vector[pair[size_t,size_t]] getNodeMap(size_t g, size_t h) except + | ||||
vector[vector[int]] getAssignmentMatrix(size_t g, size_t h) except + | vector[vector[int]] getAssignmentMatrix(size_t g, size_t h) except + | ||||
vector[vector[np.npy_uint64]] getAllMap(size_t g, size_t h) except + | |||||
vector[vector[cnp.npy_uint64]] getAllMap(size_t g, size_t h) except + | |||||
double getRuntime(size_t g, size_t h) except + | double getRuntime(size_t g, size_t h) except + | ||||
bool quasimetricCosts() except + | bool quasimetricCosts() except + | ||||
vector[vector[size_t]] hungarianLSAP(vector[vector[size_t]] matrixCost) except + | vector[vector[size_t]] hungarianLSAP(vector[vector[size_t]] matrixCost) except + | ||||
@@ -105,14 +105,16 @@ cdef extern from "src/GedLibBind.hpp" namespace "pyged": | |||||
map[string, string] getMedianEdgeLabel(vector[map[string, string]] & edge_labels) except + | map[string, string] getMedianEdgeLabel(vector[map[string, string]] & edge_labels) except + | ||||
string getInitType() except + | string getInitType() except + | ||||
# double getNodeCost(size_t label1, size_t label2) except + | # double getNodeCost(size_t label1, size_t label2) except + | ||||
void computeInducedCost(size_t g_id, size_t h_id) except + | |||||
double computeInducedCost(size_t g_id, size_t h_id) except + | |||||
############################# | ############################# | ||||
##CYTHON WRAPPER INTERFACES## | ##CYTHON WRAPPER INTERFACES## | ||||
############################# | ############################# | ||||
import numpy as np | |||||
import networkx as nx | import networkx as nx | ||||
from gklearn.ged.env import NodeMap | |||||
# import librariesImport | # import librariesImport | ||||
from ctypes import * | from ctypes import * | ||||
@@ -726,13 +728,30 @@ cdef class GEDEnv: | |||||
:type g: size_t | :type g: size_t | ||||
:type h: size_t | :type h: size_t | ||||
:return: The Node Map between the two selected graph. | :return: The Node Map between the two selected graph. | ||||
:rtype: list[tuple(size_t, size_t)] | |||||
:rtype: gklearn.ged.env.NodeMap. | |||||
.. seealso:: run_method(), get_forward_map(), get_backward_map(), get_node_image(), get_node_pre_image(), get_assignment_matrix() | .. seealso:: run_method(), get_forward_map(), get_backward_map(), get_node_image(), get_node_pre_image(), get_assignment_matrix() | ||||
.. warning:: run_method() between the same two graph must be called before this function. | .. warning:: run_method() between the same two graph must be called before this function. | ||||
.. note:: This function creates datas so use it if necessary, however you can understand how assignement works with this example. | .. note:: This function creates datas so use it if necessary, however you can understand how assignement works with this example. | ||||
""" | """ | ||||
return self.c_env.getNodeMap(g, h) | |||||
map_as_relation = self.c_env.getNodeMap(g, h) | |||||
induced_cost = self.c_env.getInducedCost(g, h) # @todo: the C++ implementation for this function in GedLibBind.ipp re-call get_node_map() once more, this is not neccessary. | |||||
source_map = [item.first if item.first < len(map_as_relation) else np.inf for item in map_as_relation] # item.first < len(map_as_relation) is not exactly correct. | |||||
# print(source_map) | |||||
target_map = [item.second if item.second < len(map_as_relation) else np.inf for item in map_as_relation] | |||||
# print(target_map) | |||||
num_node_source = len([item for item in source_map if item != np.inf]) | |||||
# print(num_node_source) | |||||
num_node_target = len([item for item in target_map if item != np.inf]) | |||||
# print(num_node_target) | |||||
node_map = NodeMap(num_node_source, num_node_target) | |||||
# print(node_map.get_forward_map(), node_map.get_backward_map()) | |||||
for i in range(len(source_map)): | |||||
node_map.add_assignment(source_map[i], target_map[i]) | |||||
node_map.set_induced_cost(induced_cost) | |||||
return node_map | |||||
def get_assignment_matrix(self, g, h) : | def get_assignment_matrix(self, g, h) : | ||||
@@ -1320,7 +1339,7 @@ cdef class GEDEnv: | |||||
return graph_id | return graph_id | ||||
def compute_induced_cost(self, g_id, h_id): | |||||
def compute_induced_cost(self, g_id, h_id, node_map): | |||||
""" | """ | ||||
Computes the edit cost between two graphs induced by a node map. | Computes the edit cost between two graphs induced by a node map. | ||||
@@ -1330,19 +1349,15 @@ cdef class GEDEnv: | |||||
ID of input graph. | ID of input graph. | ||||
h_id : int | h_id : int | ||||
ID of input graph. | ID of input graph. | ||||
node_map: gklearn.ged.env.NodeMap. | |||||
The NodeMap instance whose reduced cost will be computed and re-assigned. | |||||
Returns | Returns | ||||
------- | ------- | ||||
None. | |||||
Notes | |||||
----- | |||||
The induced edit cost of the node map between `g_id` and `h_id` is implictly computed and stored in `GEDEnv::node_maps_`. | |||||
None. | |||||
""" | """ | ||||
cost = 0.0 | |||||
self.c_env.computeInducedCost(g_id, h_id) | |||||
induced_cost = self.c_env.computeInducedCost(g_id, h_id) | |||||
node_map.set_induced_cost(induced_cost) | |||||
##################################################################### | ##################################################################### | ||||
@@ -475,8 +475,9 @@ public: | |||||
* @brief Computes the edit cost between two graphs induced by a node map. | * @brief Computes the edit cost between two graphs induced by a node map. | ||||
* @param[in] g_id ID of input graph. | * @param[in] g_id ID of input graph. | ||||
* @param[in] h_id ID of input graph. | * @param[in] h_id ID of input graph. | ||||
* @return Computed induced cost. | |||||
*/ | */ | ||||
void computeInducedCost(std::size_t g_id, std::size_t h_id) const; | |||||
double computeInducedCost(std::size_t g_id, std::size_t h_id) const; | |||||
// /*! | // /*! | ||||
// * @brief Returns node relabeling, insertion, or deletion cost. | // * @brief Returns node relabeling, insertion, or deletion cost. | ||||
@@ -492,7 +493,7 @@ public: | |||||
private: | private: | ||||
ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel> env; // environment variable | |||||
ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel> * env_; // environment variable | |||||
bool initialized; // initialization boolean (because env has one but not accessible) | bool initialized; // initialization boolean (because env has one but not accessible) | ||||
@@ -277,11 +277,16 @@ std::string toStringVectorInt(std::vector<unsigned long int> vector) { | |||||
PyGEDEnv::PyGEDEnv () { | PyGEDEnv::PyGEDEnv () { | ||||
this->env = ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel>(); | |||||
env_ = new ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel>(); | |||||
this->initialized = false; | this->initialized = false; | ||||
} | } | ||||
PyGEDEnv::~PyGEDEnv () {} | |||||
PyGEDEnv::~PyGEDEnv () { | |||||
if (env_ != NULL) { | |||||
delete env_; | |||||
env_ = NULL; | |||||
} | |||||
} | |||||
// bool initialized = false; //Initialization boolean (because Env has one but not accessible). | // bool initialized = false; //Initialization boolean (because Env has one but not accessible). | ||||
@@ -290,64 +295,68 @@ bool PyGEDEnv::isInitialized() { | |||||
} | } | ||||
void PyGEDEnv::restartEnv() { | void PyGEDEnv::restartEnv() { | ||||
this->env = ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel>(); | |||||
if (env_ != NULL) { | |||||
delete env_; | |||||
env_ = NULL; | |||||
} | |||||
env_ = new ged::GEDEnv<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel>(); | |||||
initialized = false; | initialized = false; | ||||
} | } | ||||
void PyGEDEnv::loadGXLGraph(const std::string & pathFolder, const std::string & pathXML, bool node_type, bool edge_type) { | void PyGEDEnv::loadGXLGraph(const std::string & pathFolder, const std::string & pathXML, bool node_type, bool edge_type) { | ||||
std::vector<ged::GEDGraph::GraphID> tmp_graph_ids(this->env.load_gxl_graph(pathFolder, pathXML, | |||||
std::vector<ged::GEDGraph::GraphID> tmp_graph_ids(env_->load_gxl_graph(pathFolder, pathXML, | |||||
(node_type ? ged::Options::GXLNodeEdgeType::LABELED : ged::Options::GXLNodeEdgeType::UNLABELED), | (node_type ? ged::Options::GXLNodeEdgeType::LABELED : ged::Options::GXLNodeEdgeType::UNLABELED), | ||||
(edge_type ? ged::Options::GXLNodeEdgeType::LABELED : ged::Options::GXLNodeEdgeType::UNLABELED), | (edge_type ? ged::Options::GXLNodeEdgeType::LABELED : ged::Options::GXLNodeEdgeType::UNLABELED), | ||||
std::unordered_set<std::string>(), std::unordered_set<std::string>())); | std::unordered_set<std::string>(), std::unordered_set<std::string>())); | ||||
} | } | ||||
std::pair<std::size_t,std::size_t> PyGEDEnv::getGraphIds() const { | std::pair<std::size_t,std::size_t> PyGEDEnv::getGraphIds() const { | ||||
return this->env.graph_ids(); | |||||
return env_->graph_ids(); | |||||
} | } | ||||
std::vector<std::size_t> PyGEDEnv::getAllGraphIds() { | std::vector<std::size_t> PyGEDEnv::getAllGraphIds() { | ||||
std::vector<std::size_t> listID; | std::vector<std::size_t> listID; | ||||
for (std::size_t i = this->env.graph_ids().first; i != this->env.graph_ids().second; i++) { | |||||
for (std::size_t i = env_->graph_ids().first; i != env_->graph_ids().second; i++) { | |||||
listID.push_back(i); | listID.push_back(i); | ||||
} | } | ||||
return listID; | return listID; | ||||
} | } | ||||
const std::string PyGEDEnv::getGraphClass(std::size_t id) const { | const std::string PyGEDEnv::getGraphClass(std::size_t id) const { | ||||
return this->env.get_graph_class(id); | |||||
return env_->get_graph_class(id); | |||||
} | } | ||||
const std::string PyGEDEnv::getGraphName(std::size_t id) const { | const std::string PyGEDEnv::getGraphName(std::size_t id) const { | ||||
return this->env.get_graph_name(id); | |||||
return env_->get_graph_name(id); | |||||
} | } | ||||
std::size_t PyGEDEnv::addGraph(const std::string & graph_name, const std::string & graph_class) { | std::size_t PyGEDEnv::addGraph(const std::string & graph_name, const std::string & graph_class) { | ||||
ged::GEDGraph::GraphID newId = this->env.add_graph(graph_name, graph_class); | |||||
ged::GEDGraph::GraphID newId = env_->add_graph(graph_name, graph_class); | |||||
initialized = false; | initialized = false; | ||||
return std::stoi(std::to_string(newId)); | return std::stoi(std::to_string(newId)); | ||||
} | } | ||||
void PyGEDEnv::addNode(std::size_t graphId, const std::string & nodeId, const std::map<std::string, std::string> & nodeLabel) { | void PyGEDEnv::addNode(std::size_t graphId, const std::string & nodeId, const std::map<std::string, std::string> & nodeLabel) { | ||||
this->env.add_node(graphId, nodeId, nodeLabel); | |||||
env_->add_node(graphId, nodeId, nodeLabel); | |||||
initialized = false; | initialized = false; | ||||
} | } | ||||
/*void addEdge(std::size_t graphId, ged::GXLNodeID tail, ged::GXLNodeID head, ged::GXLLabel edgeLabel) { | /*void addEdge(std::size_t graphId, ged::GXLNodeID tail, ged::GXLNodeID head, ged::GXLLabel edgeLabel) { | ||||
this->env.add_edge(graphId, tail, head, edgeLabel); | |||||
env_->add_edge(graphId, tail, head, edgeLabel); | |||||
}*/ | }*/ | ||||
void PyGEDEnv::addEdge(std::size_t graphId, const std::string & tail, const std::string & head, const std::map<std::string, std::string> & edgeLabel, bool ignoreDuplicates) { | void PyGEDEnv::addEdge(std::size_t graphId, const std::string & tail, const std::string & head, const std::map<std::string, std::string> & edgeLabel, bool ignoreDuplicates) { | ||||
this->env.add_edge(graphId, tail, head, edgeLabel, ignoreDuplicates); | |||||
env_->add_edge(graphId, tail, head, edgeLabel, ignoreDuplicates); | |||||
initialized = false; | initialized = false; | ||||
} | } | ||||
void PyGEDEnv::clearGraph(std::size_t graphId) { | void PyGEDEnv::clearGraph(std::size_t graphId) { | ||||
this->env.clear_graph(graphId); | |||||
env_->clear_graph(graphId); | |||||
initialized = false; | initialized = false; | ||||
} | } | ||||
ged::ExchangeGraph<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel> PyGEDEnv::getGraph(std::size_t graphId) const { | ged::ExchangeGraph<ged::GXLNodeID, ged::GXLLabel, ged::GXLLabel> PyGEDEnv::getGraph(std::size_t graphId) const { | ||||
return this->env.get_graph(graphId); | |||||
return env_->get_graph(graphId); | |||||
} | } | ||||
std::size_t PyGEDEnv::getGraphInternalId(std::size_t graphId) { | std::size_t PyGEDEnv::getGraphInternalId(std::size_t graphId) { | ||||
@@ -379,71 +388,71 @@ std::vector<std::vector<std::size_t>> PyGEDEnv::getGraphAdjacenceMatrix(std::siz | |||||
} | } | ||||
void PyGEDEnv::setEditCost(std::string editCost, std::vector<double> editCostConstants) { | void PyGEDEnv::setEditCost(std::string editCost, std::vector<double> editCostConstants) { | ||||
this->env.set_edit_costs(translateEditCost(editCost), editCostConstants); | |||||
env_->set_edit_costs(translateEditCost(editCost), editCostConstants); | |||||
} | } | ||||
void PyGEDEnv::setPersonalEditCost(std::vector<double> editCostConstants) { | void PyGEDEnv::setPersonalEditCost(std::vector<double> editCostConstants) { | ||||
//this->env.set_edit_costs(Your EditCost Class(editCostConstants)); | |||||
//env_->set_edit_costs(Your EditCost Class(editCostConstants)); | |||||
} | } | ||||
// void PyGEDEnv::initEnv() { | // void PyGEDEnv::initEnv() { | ||||
// this->env.init(); | |||||
// env_->init(); | |||||
// initialized = true; | // initialized = true; | ||||
// } | // } | ||||
void PyGEDEnv::initEnv(std::string initOption, bool print_to_stdout) { | void PyGEDEnv::initEnv(std::string initOption, bool print_to_stdout) { | ||||
this->env.init(translateInitOptions(initOption), print_to_stdout); | |||||
env_->init(translateInitOptions(initOption), print_to_stdout); | |||||
initialized = true; | initialized = true; | ||||
} | } | ||||
void PyGEDEnv::setMethod(std::string method, const std::string & options) { | void PyGEDEnv::setMethod(std::string method, const std::string & options) { | ||||
this->env.set_method(translateMethod(method), options); | |||||
env_->set_method(translateMethod(method), options); | |||||
} | } | ||||
void PyGEDEnv::initMethod() { | void PyGEDEnv::initMethod() { | ||||
this->env.init_method(); | |||||
env_->init_method(); | |||||
} | } | ||||
double PyGEDEnv::getInitime() const { | double PyGEDEnv::getInitime() const { | ||||
return this->env.get_init_time(); | |||||
return env_->get_init_time(); | |||||
} | } | ||||
void PyGEDEnv::runMethod(std::size_t g, std::size_t h) { | void PyGEDEnv::runMethod(std::size_t g, std::size_t h) { | ||||
this->env.run_method(g, h); | |||||
env_->run_method(g, h); | |||||
} | } | ||||
double PyGEDEnv::getUpperBound(std::size_t g, std::size_t h) const { | double PyGEDEnv::getUpperBound(std::size_t g, std::size_t h) const { | ||||
return this->env.get_upper_bound(g, h); | |||||
return env_->get_upper_bound(g, h); | |||||
} | } | ||||
double PyGEDEnv::getLowerBound(std::size_t g, std::size_t h) const { | double PyGEDEnv::getLowerBound(std::size_t g, std::size_t h) const { | ||||
return this->env.get_lower_bound(g, h); | |||||
return env_->get_lower_bound(g, h); | |||||
} | } | ||||
std::vector<long unsigned int> PyGEDEnv::getForwardMap(std::size_t g, std::size_t h) const { | std::vector<long unsigned int> PyGEDEnv::getForwardMap(std::size_t g, std::size_t h) const { | ||||
return this->env.get_node_map(g, h).get_forward_map(); | |||||
return env_->get_node_map(g, h).get_forward_map(); | |||||
} | } | ||||
std::vector<long unsigned int> PyGEDEnv::getBackwardMap(std::size_t g, std::size_t h) const { | std::vector<long unsigned int> PyGEDEnv::getBackwardMap(std::size_t g, std::size_t h) const { | ||||
return this->env.get_node_map(g, h).get_backward_map(); | |||||
return env_->get_node_map(g, h).get_backward_map(); | |||||
} | } | ||||
std::size_t PyGEDEnv::getNodeImage(std::size_t g, std::size_t h, std::size_t nodeId) const { | std::size_t PyGEDEnv::getNodeImage(std::size_t g, std::size_t h, std::size_t nodeId) const { | ||||
return this->env.get_node_map(g, h).image(nodeId); | |||||
return env_->get_node_map(g, h).image(nodeId); | |||||
} | } | ||||
std::size_t PyGEDEnv::getNodePreImage(std::size_t g, std::size_t h, std::size_t nodeId) const { | std::size_t PyGEDEnv::getNodePreImage(std::size_t g, std::size_t h, std::size_t nodeId) const { | ||||
return this->env.get_node_map(g, h).pre_image(nodeId); | |||||
return env_->get_node_map(g, h).pre_image(nodeId); | |||||
} | } | ||||
double PyGEDEnv::getInducedCost(std::size_t g, std::size_t h) const { | double PyGEDEnv::getInducedCost(std::size_t g, std::size_t h) const { | ||||
return this->env.get_node_map(g, h).induced_cost(); | |||||
return env_->get_node_map(g, h).induced_cost(); | |||||
} | } | ||||
std::vector<pair<std::size_t, std::size_t>> PyGEDEnv::getNodeMap(std::size_t g, std::size_t h) { | std::vector<pair<std::size_t, std::size_t>> PyGEDEnv::getNodeMap(std::size_t g, std::size_t h) { | ||||
std::vector<pair<std::size_t, std::size_t>> res; | std::vector<pair<std::size_t, std::size_t>> res; | ||||
std::vector<ged::NodeMap::Assignment> relation; | std::vector<ged::NodeMap::Assignment> relation; | ||||
this->env.get_node_map(g, h).as_relation(relation); | |||||
env_->get_node_map(g, h).as_relation(relation); | |||||
for (const auto & assignment : relation) { | for (const auto & assignment : relation) { | ||||
res.push_back(std::make_pair(assignment.first, assignment.second)); | res.push_back(std::make_pair(assignment.first, assignment.second)); | ||||
} | } | ||||
@@ -493,11 +502,11 @@ std::vector<std::vector<unsigned long int>> PyGEDEnv::getAllMap(std::size_t g, s | |||||
} | } | ||||
double PyGEDEnv::getRuntime(std::size_t g, std::size_t h) const { | double PyGEDEnv::getRuntime(std::size_t g, std::size_t h) const { | ||||
return this->env.get_runtime(g, h); | |||||
return env_->get_runtime(g, h); | |||||
} | } | ||||
bool PyGEDEnv::quasimetricCosts() const { | bool PyGEDEnv::quasimetricCosts() const { | ||||
return this->env.quasimetric_costs(); | |||||
return env_->quasimetric_costs(); | |||||
} | } | ||||
std::vector<std::vector<size_t>> PyGEDEnv::hungarianLSAP(std::vector<std::vector<std::size_t>> matrixCost) { | std::vector<std::vector<size_t>> PyGEDEnv::hungarianLSAP(std::vector<std::vector<std::size_t>> matrixCost) { | ||||
@@ -542,73 +551,74 @@ std::vector<std::vector<double>> PyGEDEnv::hungarianLSAPE(std::vector<std::vecto | |||||
} | } | ||||
std::size_t PyGEDEnv::getNumNodeLabels() const { | std::size_t PyGEDEnv::getNumNodeLabels() const { | ||||
return this->env.num_node_labels(); | |||||
return env_->num_node_labels(); | |||||
} | } | ||||
std::map<std::string, std::string> PyGEDEnv::getNodeLabel(std::size_t label_id) const { | std::map<std::string, std::string> PyGEDEnv::getNodeLabel(std::size_t label_id) const { | ||||
return this->env.get_node_label(label_id); | |||||
return env_->get_node_label(label_id); | |||||
} | } | ||||
std::size_t PyGEDEnv::getNumEdgeLabels() const { | std::size_t PyGEDEnv::getNumEdgeLabels() const { | ||||
return this->env.num_edge_labels(); | |||||
return env_->num_edge_labels(); | |||||
} | } | ||||
std::map<std::string, std::string> PyGEDEnv::getEdgeLabel(std::size_t label_id) const { | std::map<std::string, std::string> PyGEDEnv::getEdgeLabel(std::size_t label_id) const { | ||||
return this->env.get_edge_label(label_id); | |||||
return env_->get_edge_label(label_id); | |||||
} | } | ||||
// std::size_t PyGEDEnv::getNumNodes(std::size_t graph_id) const { | // std::size_t PyGEDEnv::getNumNodes(std::size_t graph_id) const { | ||||
// return this->env.get_num_nodes(graph_id); | |||||
// return env_->get_num_nodes(graph_id); | |||||
// } | // } | ||||
double PyGEDEnv::getAvgNumNodes() const { | double PyGEDEnv::getAvgNumNodes() const { | ||||
return this->env.get_avg_num_nodes(); | |||||
return env_->get_avg_num_nodes(); | |||||
} | } | ||||
double PyGEDEnv::getNodeRelCost(const std::map<std::string, std::string> & node_label_1, const std::map<std::string, std::string> & node_label_2) const { | double PyGEDEnv::getNodeRelCost(const std::map<std::string, std::string> & node_label_1, const std::map<std::string, std::string> & node_label_2) const { | ||||
return this->env.node_rel_cost(node_label_1, node_label_2); | |||||
return env_->node_rel_cost(node_label_1, node_label_2); | |||||
} | } | ||||
double PyGEDEnv::getNodeDelCost(const std::map<std::string, std::string> & node_label) const { | double PyGEDEnv::getNodeDelCost(const std::map<std::string, std::string> & node_label) const { | ||||
return this->env.node_del_cost(node_label); | |||||
return env_->node_del_cost(node_label); | |||||
} | } | ||||
double PyGEDEnv::getNodeInsCost(const std::map<std::string, std::string> & node_label) const { | double PyGEDEnv::getNodeInsCost(const std::map<std::string, std::string> & node_label) const { | ||||
return this->env.node_ins_cost(node_label); | |||||
return env_->node_ins_cost(node_label); | |||||
} | } | ||||
std::map<std::string, std::string> PyGEDEnv::getMedianNodeLabel(const std::vector<std::map<std::string, std::string>> & node_labels) const { | std::map<std::string, std::string> PyGEDEnv::getMedianNodeLabel(const std::vector<std::map<std::string, std::string>> & node_labels) const { | ||||
return this->env.median_node_label(node_labels); | |||||
return env_->median_node_label(node_labels); | |||||
} | } | ||||
double PyGEDEnv::getEdgeRelCost(const std::map<std::string, std::string> & edge_label_1, const std::map<std::string, std::string> & edge_label_2) const { | double PyGEDEnv::getEdgeRelCost(const std::map<std::string, std::string> & edge_label_1, const std::map<std::string, std::string> & edge_label_2) const { | ||||
return this->env.edge_rel_cost(edge_label_1, edge_label_2); | |||||
return env_->edge_rel_cost(edge_label_1, edge_label_2); | |||||
} | } | ||||
double PyGEDEnv::getEdgeDelCost(const std::map<std::string, std::string> & edge_label) const { | double PyGEDEnv::getEdgeDelCost(const std::map<std::string, std::string> & edge_label) const { | ||||
return this->env.edge_del_cost(edge_label); | |||||
return env_->edge_del_cost(edge_label); | |||||
} | } | ||||
double PyGEDEnv::getEdgeInsCost(const std::map<std::string, std::string> & edge_label) const { | double PyGEDEnv::getEdgeInsCost(const std::map<std::string, std::string> & edge_label) const { | ||||
return this->env.edge_ins_cost(edge_label); | |||||
return env_->edge_ins_cost(edge_label); | |||||
} | } | ||||
std::map<std::string, std::string> PyGEDEnv::getMedianEdgeLabel(const std::vector<std::map<std::string, std::string>> & edge_labels) const { | std::map<std::string, std::string> PyGEDEnv::getMedianEdgeLabel(const std::vector<std::map<std::string, std::string>> & edge_labels) const { | ||||
return this->env.median_edge_label(edge_labels); | |||||
return env_->median_edge_label(edge_labels); | |||||
} | } | ||||
std::string PyGEDEnv::getInitType() const { | std::string PyGEDEnv::getInitType() const { | ||||
return initOptionsToString(this->env.get_init_type()); | |||||
return initOptionsToString(env_->get_init_type()); | |||||
} | } | ||||
void PyGEDEnv::computeInducedCost(std::size_t g_id, std::size_t h_id) const { | |||||
ged::NodeMap node_map = this->env.get_node_map(g_id, h_id); | |||||
this->env.compute_induced_cost(g_id, h_id, node_map); | |||||
double PyGEDEnv::computeInducedCost(std::size_t g_id, std::size_t h_id) const { | |||||
ged::NodeMap node_map = env_->get_node_map(g_id, h_id); | |||||
env_->compute_induced_cost(g_id, h_id, node_map); | |||||
return node_map.induced_cost(); | |||||
} | } | ||||
// double PyGEDEnv::getNodeCost(std::size_t label1, std::size_t label2) const { | // double PyGEDEnv::getNodeCost(std::size_t label1, std::size_t label2) const { | ||||
// return this->env.ged_data_node_cost(label1, label2); | |||||
// return env_->ged_data_node_cost(label1, label2); | |||||
// } | // } | ||||
@@ -630,7 +640,7 @@ void PyGEDEnv::computeInducedCost(std::size_t g_id, std::size_t h_id) const { | |||||
/*loadGXLGraph(pathFolder, pathXML); | /*loadGXLGraph(pathFolder, pathXML); | ||||
std::vector<std::size_t> graph_ids = getAllGraphIds(); | std::vector<std::size_t> graph_ids = getAllGraphIds(); | ||||
std::size_t median_id = this->env.add_graph("median", ""); | |||||
std::size_t median_id = env_->add_graph("median", ""); | |||||
initEnv(initOption); | initEnv(initOption); | ||||
@@ -640,10 +650,10 @@ void PyGEDEnv::computeInducedCost(std::size_t g_id, std::size_t h_id) const { | |||||
median_estimator.set_options("--init-type RANDOM --randomness PSEUDO --seed " + seed); | median_estimator.set_options("--init-type RANDOM --randomness PSEUDO --seed " + seed); | ||||
median_estimator.run(graph_ids, median_id); | median_estimator.run(graph_ids, median_id); | ||||
std::string gxl_file_name("../output/gen_median_Letter_HIGH_" + letter_class + ".gxl"); | std::string gxl_file_name("../output/gen_median_Letter_HIGH_" + letter_class + ".gxl"); | ||||
this->env.save_as_gxl_graph(median_id, gxl_file_name);*/ | |||||
env_->save_as_gxl_graph(median_id, gxl_file_name);*/ | |||||
/*std::string tikz_file_name("../output/gen_median_Letter_HIGH_" + letter_class + ".tex"); | /*std::string tikz_file_name("../output/gen_median_Letter_HIGH_" + letter_class + ".tex"); | ||||
save_letter_graph_as_tikz_file(this->env.get_graph(median_id), tikz_file_name);*/ | |||||
save_letter_graph_as_tikz_file(env_->get_graph(median_id), tikz_file_name);*/ | |||||
//} | //} | ||||
} | } | ||||
@@ -12,6 +12,132 @@ from gklearn.preimage.utils import generate_median_preimages_by_class | |||||
from gklearn.utils import compute_gram_matrices_by_class | from gklearn.utils import compute_gram_matrices_by_class | ||||
def xp_median_preimage_13_1(): | |||||
"""xp 13_1: PAH, StructuralSP, using NON_SYMBOLIC. | |||||
""" | |||||
# set parameters. | |||||
ds_name = 'PAH' # | |||||
mpg_options = {'fit_method': 'k-graphs', | |||||
'init_ecc': [3, 3, 1, 3, 3, 0], # | |||||
'ds_name': ds_name, | |||||
'parallel': True, # False | |||||
'time_limit_in_sec': 0, | |||||
'max_itrs': 100, # | |||||
'max_itrs_without_update': 3, | |||||
'epsilon_residual': 0.01, | |||||
'epsilon_ec': 0.1, | |||||
'verbose': 2} | |||||
mixkernel = functools.partial(kernelproduct, deltakernel, gaussiankernel) | |||||
sub_kernels = {'symb': deltakernel, 'nsymb': gaussiankernel, 'mix': mixkernel} | |||||
kernel_options = {'name': 'StructuralSP', | |||||
'edge_weight': None, | |||||
'node_kernels': sub_kernels, | |||||
'edge_kernels': sub_kernels, | |||||
'compute_method': 'naive', | |||||
'parallel': 'imap_unordered', | |||||
# 'parallel': None, | |||||
'n_jobs': multiprocessing.cpu_count(), | |||||
'normalize': True, | |||||
'verbose': 2} | |||||
ged_options = {'method': 'IPFP', | |||||
'initialization_method': 'RANDOM', # 'NODE' | |||||
'initial_solutions': 10, # 1 | |||||
'edit_cost': 'NON_SYMBOLIC', # | |||||
'attr_distance': 'euclidean', | |||||
'ratio_runs_from_initial_solutions': 1, | |||||
'threads': multiprocessing.cpu_count(), | |||||
'init_option': 'EAGER_WITHOUT_SHUFFLED_COPIES'} | |||||
mge_options = {'init_type': 'MEDOID', | |||||
'random_inits': 10, | |||||
'time_limit': 600, | |||||
'verbose': 2, | |||||
'refine': False} | |||||
save_results = True | |||||
dir_save = '../results/xp_median_preimage/' + ds_name + '.' + kernel_options['name'] + '/' | |||||
irrelevant_labels = None # | |||||
edge_required = False # | |||||
# print settings. | |||||
print('parameters:') | |||||
print('dataset name:', ds_name) | |||||
print('mpg_options:', mpg_options) | |||||
print('kernel_options:', kernel_options) | |||||
print('ged_options:', ged_options) | |||||
print('mge_options:', mge_options) | |||||
print('save_results:', save_results) | |||||
print('irrelevant_labels:', irrelevant_labels) | |||||
print() | |||||
# generate preimages. | |||||
for fit_method in ['k-graphs'] + ['random'] * 5: | |||||
print('\n-------------------------------------') | |||||
print('fit method:', fit_method, '\n') | |||||
mpg_options['fit_method'] = fit_method | |||||
generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged_options, mge_options, save_results=save_results, save_medians=True, plot_medians=True, load_gm='auto', dir_save=dir_save, irrelevant_labels=irrelevant_labels, edge_required=edge_required) | |||||
def xp_median_preimage_13_2(): | |||||
"""xp 13_2: PAH, ShortestPath, using NON_SYMBOLIC. | |||||
""" | |||||
# set parameters. | |||||
ds_name = 'PAH' # | |||||
mpg_options = {'fit_method': 'k-graphs', | |||||
'init_ecc': [3, 3, 1, 3, 3, 0], # | |||||
'ds_name': ds_name, | |||||
'parallel': True, # False | |||||
'time_limit_in_sec': 0, | |||||
'max_itrs': 100, | |||||
'max_itrs_without_update': 3, | |||||
'epsilon_residual': 0.01, | |||||
'epsilon_ec': 0.1, | |||||
'verbose': 2} | |||||
mixkernel = functools.partial(kernelproduct, deltakernel, gaussiankernel) | |||||
sub_kernels = {'symb': deltakernel, 'nsymb': gaussiankernel, 'mix': mixkernel} | |||||
kernel_options = {'name': 'ShortestPath', | |||||
'edge_weight': None, | |||||
'node_kernels': sub_kernels, | |||||
'parallel': 'imap_unordered', | |||||
# 'parallel': None, | |||||
'n_jobs': multiprocessing.cpu_count(), | |||||
'normalize': True, | |||||
'verbose': 2} | |||||
ged_options = {'method': 'IPFP', | |||||
'initialization_method': 'RANDOM', # 'NODE' | |||||
'initial_solutions': 10, # 1 | |||||
'edit_cost': 'NON_SYMBOLIC', # | |||||
'attr_distance': 'euclidean', | |||||
'ratio_runs_from_initial_solutions': 1, | |||||
'threads': multiprocessing.cpu_count(), | |||||
'init_option': 'EAGER_WITHOUT_SHUFFLED_COPIES'} | |||||
mge_options = {'init_type': 'MEDOID', | |||||
'random_inits': 10, | |||||
'time_limit': 600, | |||||
'verbose': 2, | |||||
'refine': False} | |||||
save_results = True | |||||
dir_save = '../results/xp_median_preimage/' + ds_name + '.' + kernel_options['name'] + '/' # | |||||
irrelevant_labels = None # | |||||
edge_required = True # | |||||
# print settings. | |||||
print('parameters:') | |||||
print('dataset name:', ds_name) | |||||
print('mpg_options:', mpg_options) | |||||
print('kernel_options:', kernel_options) | |||||
print('ged_options:', ged_options) | |||||
print('mge_options:', mge_options) | |||||
print('save_results:', save_results) | |||||
print('irrelevant_labels:', irrelevant_labels) | |||||
print() | |||||
# generate preimages. | |||||
for fit_method in ['k-graphs'] + ['random'] * 5: # | |||||
print('\n-------------------------------------') | |||||
print('fit method:', fit_method, '\n') | |||||
mpg_options['fit_method'] = fit_method | |||||
generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged_options, mge_options, save_results=save_results, save_medians=True, plot_medians=True, load_gm='auto', dir_save=dir_save, irrelevant_labels=irrelevant_labels, edge_required=edge_required) | |||||
def xp_median_preimage_12_1(): | def xp_median_preimage_12_1(): | ||||
"""xp 12_1: PAH, StructuralSP, using NON_SYMBOLIC, unlabeled. | """xp 12_1: PAH, StructuralSP, using NON_SYMBOLIC, unlabeled. | ||||
""" | """ | ||||
@@ -1969,7 +2095,13 @@ if __name__ == "__main__": | |||||
# xp_median_preimage_12_4() | # xp_median_preimage_12_4() | ||||
#### xp 12_5: PAH, ShortestPath, using NON_SYMBOLIC, unlabeled. | #### xp 12_5: PAH, ShortestPath, using NON_SYMBOLIC, unlabeled. | ||||
xp_median_preimage_12_5() | |||||
# xp_median_preimage_12_5() | |||||
#### xp 13_1: PAH, StructuralSP, using NON_SYMBOLIC. | |||||
xp_median_preimage_13_1() | |||||
#### xp 13_2: PAH, ShortestPath, using NON_SYMBOLIC. | |||||
# xp_median_preimage_13_2() | |||||
@@ -88,7 +88,7 @@ class MedianPreimageGenerator(PreimageGenerator): | |||||
node_attrs=self._dataset.node_attrs, | node_attrs=self._dataset.node_attrs, | ||||
edge_attrs=self._dataset.edge_attrs, | edge_attrs=self._dataset.edge_attrs, | ||||
ds_infos=self._dataset.get_dataset_infos(keys=['directed']), | ds_infos=self._dataset.get_dataset_infos(keys=['directed']), | ||||
**self._kernel_options) | |||||
kernel_options=self._kernel_options) | |||||
# record start time. | # record start time. | ||||
start = time.time() | start = time.time() | ||||
@@ -25,7 +25,7 @@ import networkx as nx | |||||
import os | import os | ||||
def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged_options, mge_options, save_results=True, save_medians=True, plot_medians=True, load_gm='auto', dir_save='', irrelevant_labels=None, edge_required=False): | |||||
def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged_options, mge_options, save_results=True, save_medians=True, plot_medians=True, load_gm='auto', dir_save='', irrelevant_labels=None, edge_required=False, cut_range=None): | |||||
import os.path | import os.path | ||||
from gklearn.preimage import MedianPreimageGenerator | from gklearn.preimage import MedianPreimageGenerator | ||||
from gklearn.utils import split_dataset_by_target | from gklearn.utils import split_dataset_by_target | ||||
@@ -38,7 +38,8 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||||
dataset_all.trim_dataset(edge_required=edge_required) | dataset_all.trim_dataset(edge_required=edge_required) | ||||
if irrelevant_labels is not None: | if irrelevant_labels is not None: | ||||
dataset_all.remove_labels(**irrelevant_labels) | dataset_all.remove_labels(**irrelevant_labels) | ||||
# dataset_all.cut_graphs(range(0, 10)) | |||||
if cut_range is not None: | |||||
dataset_all.cut_graphs(cut_range) | |||||
datasets = split_dataset_by_target(dataset_all) | datasets = split_dataset_by_target(dataset_all) | ||||
if save_results: | if save_results: | ||||
@@ -56,13 +56,14 @@ class Dataset(object): | |||||
self.__node_attrs = label_names['node_attrs'] | self.__node_attrs = label_names['node_attrs'] | ||||
self.__edge_labels = label_names['edge_labels'] | self.__edge_labels = label_names['edge_labels'] | ||||
self.__edge_attrs = label_names['edge_attrs'] | self.__edge_attrs = label_names['edge_attrs'] | ||||
self.clean_labels() | |||||
def load_graphs(self, graphs, targets=None): | def load_graphs(self, graphs, targets=None): | ||||
# this has to be followed by set_labels(). | # this has to be followed by set_labels(). | ||||
self.__graphs = graphs | self.__graphs = graphs | ||||
self.__targets = targets | self.__targets = targets | ||||
# self.set_labels_attrs() | |||||
# self.set_labels_attrs() # @todo | |||||
def load_predefined_dataset(self, ds_name): | def load_predefined_dataset(self, ds_name): | ||||
@@ -128,6 +129,7 @@ class Dataset(object): | |||||
self.__node_attrs = label_names['node_attrs'] | self.__node_attrs = label_names['node_attrs'] | ||||
self.__edge_labels = label_names['edge_labels'] | self.__edge_labels = label_names['edge_labels'] | ||||
self.__edge_attrs = label_names['edge_attrs'] | self.__edge_attrs = label_names['edge_attrs'] | ||||
self.clean_labels() | |||||
def set_labels(self, node_labels=[], node_attrs=[], edge_labels=[], edge_attrs=[]): | def set_labels(self, node_labels=[], node_attrs=[], edge_labels=[], edge_attrs=[]): | ||||
@@ -141,27 +143,27 @@ class Dataset(object): | |||||
# @todo: remove labels which have only one possible values. | # @todo: remove labels which have only one possible values. | ||||
if node_labels is None: | if node_labels is None: | ||||
self.__node_labels = self.__graphs[0].graph['node_labels'] | self.__node_labels = self.__graphs[0].graph['node_labels'] | ||||
# # graphs are considered node unlabeled if all nodes have the same label. | |||||
# infos.update({'node_labeled': is_nl if node_label_num > 1 else False}) | |||||
# # graphs are considered node unlabeled if all nodes have the same label. | |||||
# infos.update({'node_labeled': is_nl if node_label_num > 1 else False}) | |||||
if node_attrs is None: | if node_attrs is None: | ||||
self.__node_attrs = self.__graphs[0].graph['node_attrs'] | self.__node_attrs = self.__graphs[0].graph['node_attrs'] | ||||
# for G in Gn: | |||||
# for n in G.nodes(data=True): | |||||
# if 'attributes' in n[1]: | |||||
# return len(n[1]['attributes']) | |||||
# return 0 | |||||
# for G in Gn: | |||||
# for n in G.nodes(data=True): | |||||
# if 'attributes' in n[1]: | |||||
# return len(n[1]['attributes']) | |||||
# return 0 | |||||
if edge_labels is None: | if edge_labels is None: | ||||
self.__edge_labels = self.__graphs[0].graph['edge_labels'] | self.__edge_labels = self.__graphs[0].graph['edge_labels'] | ||||
# # graphs are considered edge unlabeled if all edges have the same label. | |||||
# infos.update({'edge_labeled': is_el if edge_label_num > 1 else False}) | |||||
# # graphs are considered edge unlabeled if all edges have the same label. | |||||
# infos.update({'edge_labeled': is_el if edge_label_num > 1 else False}) | |||||
if edge_attrs is None: | if edge_attrs is None: | ||||
self.__edge_attrs = self.__graphs[0].graph['edge_attrs'] | self.__edge_attrs = self.__graphs[0].graph['edge_attrs'] | ||||
# for G in Gn: | |||||
# if nx.number_of_edges(G) > 0: | |||||
# for e in G.edges(data=True): | |||||
# if 'attributes' in e[2]: | |||||
# return len(e[2]['attributes']) | |||||
# return 0 | |||||
# for G in Gn: | |||||
# if nx.number_of_edges(G) > 0: | |||||
# for e in G.edges(data=True): | |||||
# if 'attributes' in e[2]: | |||||
# return len(e[2]['attributes']) | |||||
# return 0 | |||||
def get_dataset_infos(self, keys=None): | def get_dataset_infos(self, keys=None): | ||||
@@ -326,7 +328,7 @@ class Dataset(object): | |||||
if self.__node_label_nums is None: | if self.__node_label_nums is None: | ||||
self.__node_label_nums = {} | self.__node_label_nums = {} | ||||
for node_label in self.__node_labels: | for node_label in self.__node_labels: | ||||
self.__node_label_nums[node_label] = self.get_node_label_num(node_label) | |||||
self.__node_label_nums[node_label] = self.__get_node_label_num(node_label) | |||||
infos['node_label_nums'] = self.__node_label_nums | infos['node_label_nums'] = self.__node_label_nums | ||||
if 'edge_label_dim' in keys: | if 'edge_label_dim' in keys: | ||||
@@ -338,7 +340,7 @@ class Dataset(object): | |||||
if self.__edge_label_nums is None: | if self.__edge_label_nums is None: | ||||
self.__edge_label_nums = {} | self.__edge_label_nums = {} | ||||
for edge_label in self.__edge_labels: | for edge_label in self.__edge_labels: | ||||
self.__edge_label_nums[edge_label] = self.get_edge_label_num(edge_label) | |||||
self.__edge_label_nums[edge_label] = self.__get_edge_label_num(edge_label) | |||||
infos['edge_label_nums'] = self.__edge_label_nums | infos['edge_label_nums'] = self.__edge_label_nums | ||||
if 'directed' in keys or 'substructures' in keys: | if 'directed' in keys or 'substructures' in keys: | ||||
@@ -417,30 +419,87 @@ class Dataset(object): | |||||
for g in self.__graphs: | for g in self.__graphs: | ||||
for nd in g.nodes(): | for nd in g.nodes(): | ||||
for nl in node_labels: | for nl in node_labels: | ||||
del g.nodes[nd][nl] | |||||
del g.nodes[nd][nl] | |||||
for na in node_attrs: | for na in node_attrs: | ||||
del g.nodes[nd][na] | del g.nodes[nd][na] | ||||
for ed in g.edges(): | for ed in g.edges(): | ||||
for el in edge_labels: | for el in edge_labels: | ||||
del g.edges[ed][el] | |||||
del g.edges[ed][el] | |||||
for ea in edge_attrs: | for ea in edge_attrs: | ||||
del g.edges[ed][ea] | |||||
del g.edges[ed][ea] | |||||
if len(node_labels) > 0: | if len(node_labels) > 0: | ||||
self.__node_labels = [nl for nl in self.__node_labels if nl not in node_labels] | |||||
self.__node_labels = [nl for nl in self.__node_labels if nl not in node_labels] | |||||
if len(edge_labels) > 0: | if len(edge_labels) > 0: | ||||
self.__edge_labels = [el for el in self.__edge_labels if el not in edge_labels] | |||||
self.__edge_labels = [el for el in self.__edge_labels if el not in edge_labels] | |||||
if len(node_attrs) > 0: | if len(node_attrs) > 0: | ||||
self.__node_attrs = [na for na in self.__node_attrs if na not in node_attrs] | |||||
self.__node_attrs = [na for na in self.__node_attrs if na not in node_attrs] | |||||
if len(edge_attrs) > 0: | if len(edge_attrs) > 0: | ||||
self.__edge_attrs = [ea for ea in self.__edge_attrs if ea not in edge_attrs] | |||||
self.__edge_attrs = [ea for ea in self.__edge_attrs if ea not in edge_attrs] | |||||
def clean_labels(self): | |||||
labels = [] | |||||
for name in self.__node_labels: | |||||
label = set() | |||||
for G in self.__graphs: | |||||
label = label | set(nx.get_node_attributes(G, name).values()) | |||||
if len(label) > 1: | |||||
labels.append(name) | |||||
break | |||||
if len(label) < 2: | |||||
for G in self.__graphs: | |||||
for nd in G.nodes(): | |||||
del G.nodes[nd][name] | |||||
self.__node_labels = labels | |||||
labels = [] | |||||
for name in self.__edge_labels: | |||||
label = set() | |||||
for G in self.__graphs: | |||||
label = label | set(nx.get_edge_attributes(G, name).values()) | |||||
if len(label) > 1: | |||||
labels.append(name) | |||||
break | |||||
if len(label) < 2: | |||||
for G in self.__graphs: | |||||
for ed in G.edges(): | |||||
del G.edges[ed][name] | |||||
self.__edge_labels = labels | |||||
labels = [] | |||||
for name in self.__node_attrs: | |||||
label = set() | |||||
for G in self.__graphs: | |||||
label = label | set(nx.get_node_attributes(G, name).values()) | |||||
if len(label) > 1: | |||||
labels.append(name) | |||||
break | |||||
if len(label) < 2: | |||||
for G in self.__graphs: | |||||
for nd in G.nodes(): | |||||
del G.nodes[nd][name] | |||||
self.__node_attrs = labels | |||||
labels = [] | |||||
for name in self.__edge_attrs: | |||||
label = set() | |||||
for G in self.__graphs: | |||||
label = label | set(nx.get_edge_attributes(G, name).values()) | |||||
if len(label) > 1: | |||||
labels.append(name) | |||||
break | |||||
if len(label) < 2: | |||||
for G in self.__graphs: | |||||
for ed in G.edges(): | |||||
del G.edges[ed][name] | |||||
self.__edge_attrs = labels | |||||
def cut_graphs(self, range_): | def cut_graphs(self, range_): | ||||
self.__graphs = [self.__graphs[i] for i in range_] | self.__graphs = [self.__graphs[i] for i in range_] | ||||
if self.__targets is not None: | if self.__targets is not None: | ||||
self.__targets = [self.__targets[i] for i in range_] | self.__targets = [self.__targets[i] for i in range_] | ||||
# @todo | |||||
# self.set_labels_attrs() | |||||
self.clean_labels() | |||||
def trim_dataset(self, edge_required=False): | def trim_dataset(self, edge_required=False): | ||||
@@ -451,8 +510,7 @@ class Dataset(object): | |||||
idx = [p[0] for p in trimed_pairs] | idx = [p[0] for p in trimed_pairs] | ||||
self.__graphs = [p[1] for p in trimed_pairs] | self.__graphs = [p[1] for p in trimed_pairs] | ||||
self.__targets = [self.__targets[i] for i in idx] | self.__targets = [self.__targets[i] for i in idx] | ||||
# @todo | |||||
# self.set_labels_attrs() | |||||
self.clean_labels() | |||||
def __get_dataset_size(self): | def __get_dataset_size(self): | ||||
@@ -655,4 +713,5 @@ def split_dataset_by_target(dataset): | |||||
sub_dataset.load_graphs(sub_graphs, [key] * len(val)) | sub_dataset.load_graphs(sub_graphs, [key] * len(val)) | ||||
sub_dataset.set_labels(node_labels=dataset.node_labels, node_attrs=dataset.node_attrs, edge_labels=dataset.edge_labels, edge_attrs=dataset.edge_attrs) | sub_dataset.set_labels(node_labels=dataset.node_labels, node_attrs=dataset.node_attrs, edge_labels=dataset.edge_labels, edge_attrs=dataset.edge_attrs) | ||||
datasets.append(sub_dataset) | datasets.append(sub_dataset) | ||||
# @todo: clean_labels? | |||||
return datasets | return datasets |