@@ -15,7 +15,7 @@ import sys | |||
import networkx as nx | |||
class MedianGraphEstimator(object): | |||
class MedianGraphEstimator(object): # @todo: differ dummy_node from undifined node? | |||
def __init__(self, ged_env, constant_node_costs): | |||
"""Constructor. | |||
@@ -377,7 +377,7 @@ class MedianGraphEstimator(object): | |||
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) | |||
print(self.__best_init_sum_of_distances) | |||
# print(self.__best_init_sum_of_distances) | |||
# Print information about current iteration. | |||
if self.__print_to_stdout == 2: | |||
@@ -400,7 +400,7 @@ class MedianGraphEstimator(object): | |||
decreased_order = False | |||
increased_order = False | |||
# Update the median. # @todo!!!!!!!!!!!!!!!!!!!!!! | |||
# Update the median. | |||
median_modified = self.__update_median(graphs, median) | |||
if self.__update_order: | |||
if not median_modified or self.__itrs[median_pos] == 0: | |||
@@ -434,6 +434,7 @@ class MedianGraphEstimator(object): | |||
# Compute induced costs of the old node maps w.r.t. the updated median. | |||
for graph_id in graph_ids: | |||
# print(self.__node_maps_from_median[graph_id].induced_cost()) | |||
# xxx = self.__node_maps_from_median[graph_id] | |||
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()) | |||
@@ -444,7 +445,7 @@ class MedianGraphEstimator(object): | |||
print('done.') | |||
# Update the node maps. | |||
node_maps_modified = self.__update_node_maps() # @todo | |||
node_maps_modified = self.__update_node_maps() | |||
# Update the order of the median if no improvement can be found with the current order. | |||
@@ -592,6 +593,44 @@ class MedianGraphEstimator(object): | |||
if state == 'converged': | |||
return self.__converged_sum_of_distances | |||
return self.__sum_of_distances | |||
def get_runtime(self, state): | |||
if not self.__median_available(): | |||
raise Exception('No median has been computed. Call run() before calling get_runtime().') | |||
if state == AlgorithmState.INITIALIZED: | |||
return self.__runtime_initialized | |||
if state == AlgorithmState.CONVERGED: | |||
return self.__runtime_converged | |||
return self.__runtime | |||
def get_num_itrs(self): | |||
if not self.__median_available(): | |||
raise Exception('No median has been computed. Call run() before calling get_num_itrs().') | |||
return self.__itrs | |||
def get_num_times_order_decreased(self): | |||
if not self.__median_available(): | |||
raise Exception('No median has been computed. Call run() before calling get_num_times_order_decreased().') | |||
return self.__num_decrease_order | |||
def get_num_times_order_increased(self): | |||
if not self.__median_available(): | |||
raise Exception('No median has been computed. Call run() before calling get_num_times_order_increased().') | |||
return self.__num_increase_order | |||
def get_num_converged_descents(self): | |||
if not self.__median_available(): | |||
raise Exception('No median has been computed. Call run() before calling get_num_converged_descents().') | |||
return self.__num_converged_descents | |||
def get_ged_env(self): | |||
return self.__ged_env | |||
def __set_default_options(self): | |||
@@ -814,7 +853,7 @@ class MedianGraphEstimator(object): | |||
decreased_order = False | |||
# 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: # @todo | |||
while self.__compute_best_deletion_delta(graphs, median, id_deleted_node) < -self.__epsilon: | |||
decreased_order = True | |||
median = self.__delete_node_from_median(id_deleted_node[0], median) | |||
@@ -896,7 +935,7 @@ class MedianGraphEstimator(object): | |||
increased_order = False | |||
# Increase the order as long as the best insertion delta is negative. | |||
while self.__compute_best_insertion_delta(graphs, best_config, best_label) > - self.__epsilon: | |||
while self.__compute_best_insertion_delta(graphs, best_config, best_label) < - self.__epsilon: # @todo | |||
increased_order = True | |||
self.__add_node_to_median(best_config, best_label, median) | |||
@@ -155,5 +155,5 @@ def test_median_graph_estimator_symb(): | |||
if __name__ == '__main__': | |||
set_median, gen_median = test_median_graph_estimator() | |||
# set_median, gen_median = test_median_graph_estimator_symb() | |||
# set_median, gen_median = test_median_graph_estimator() | |||
set_median, gen_median = test_median_graph_estimator_symb() |
@@ -105,7 +105,7 @@ cdef extern from "src/GedLibBind.hpp" namespace "pyged": | |||
map[string, string] getMedianEdgeLabel(vector[map[string, string]] & edge_labels) except + | |||
string getInitType() except + | |||
# double getNodeCost(size_t label1, size_t label2) except + | |||
double computeInducedCost(size_t g_id, size_t h_id) except + | |||
double computeInducedCost(size_t g_id, size_t h_id, vector[pair[size_t,size_t]]) except + | |||
############################# | |||
@@ -1356,7 +1356,17 @@ cdef class GEDEnv: | |||
------- | |||
None. | |||
""" | |||
induced_cost = self.c_env.computeInducedCost(g_id, h_id) | |||
relation = [] | |||
node_map.as_relation(relation) | |||
# print(relation) | |||
dummy_node = get_dummy_node() | |||
# print(dummy_node) | |||
for i, val in enumerate(relation): | |||
val1 = dummy_node if val[0] == np.inf else val[0] | |||
val2 = dummy_node if val[1] == np.inf else val[1] | |||
relation[i] = tuple((val1, val2)) | |||
# print(relation) | |||
induced_cost = self.c_env.computeInducedCost(g_id, h_id, relation) | |||
node_map.set_induced_cost(induced_cost) | |||
@@ -477,7 +477,7 @@ public: | |||
* @param[in] h_id ID of input graph. | |||
* @return Computed induced cost. | |||
*/ | |||
double computeInducedCost(std::size_t g_id, std::size_t h_id) const; | |||
double computeInducedCost(std::size_t g_id, std::size_t h_id, std::vector<pair<std::size_t, std::size_t>> relation) const; | |||
// /*! | |||
// * @brief Returns node relabeling, insertion, or deletion cost. | |||
@@ -610,13 +610,38 @@ std::string PyGEDEnv::getInitType() const { | |||
return initOptionsToString(env_->get_init_type()); | |||
} | |||
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); | |||
double PyGEDEnv::computeInducedCost(std::size_t g_id, std::size_t h_id, std::vector<pair<std::size_t, std::size_t>> relation) const { | |||
ged::NodeMap node_map = ged::NodeMap(env_->get_num_nodes(g_id), env_->get_num_nodes(h_id)); | |||
for (const auto & assignment : relation) { | |||
node_map.add_assignment(assignment.first, assignment.second); | |||
// std::cout << assignment.first << assignment.second << endl; | |||
} | |||
const std::vector<ged::GEDGraph::NodeID> forward_map = node_map.get_forward_map(); | |||
for (std::size_t i{0}; i < node_map.num_source_nodes(); i++) { | |||
if (forward_map.at(i) == ged::GEDGraph::undefined_node()) { | |||
node_map.add_assignment(i, ged::GEDGraph::dummy_node()); | |||
} | |||
} | |||
const std::vector<ged::GEDGraph::NodeID> backward_map = node_map.get_backward_map(); | |||
for (std::size_t i{0}; i < node_map.num_target_nodes(); i++) { | |||
if (backward_map.at(i) == ged::GEDGraph::undefined_node()) { | |||
node_map.add_assignment(ged::GEDGraph::dummy_node(), i); | |||
} | |||
} | |||
// for (auto & map : node_map.get_forward_map()) { | |||
// std::cout << map << ", "; | |||
// } | |||
// std::cout << endl; | |||
// for (auto & map : node_map.get_backward_map()) { | |||
// std::cout << map << ", "; | |||
// } | |||
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 { | |||
// return env_->ged_data_node_cost(label1, label2); | |||
// } | |||
@@ -187,6 +187,10 @@ class MedianPreimageGenerator(PreimageGenerator): | |||
results['itrs'] = self.__itrs | |||
results['converged'] = self.__converged | |||
results['num_updates_ecc'] = self.__num_updates_ecc | |||
results['mge'] = {} | |||
results['mge']['num_decrease_order'] = self.__mge.get_num_times_order_decreased() | |||
results['mge']['num_increase_order'] = self.__mge.get_num_times_order_increased() | |||
results['mge']['num_converged_descents'] = self.__mge.get_num_converged_descents() | |||
return results | |||
@@ -660,27 +664,27 @@ class MedianPreimageGenerator(PreimageGenerator): | |||
ged_env.init(init_option=self.__ged_options['init_option']) | |||
# Set up the madian graph estimator. | |||
mge = MedianGraphEstimator(ged_env, constant_node_costs(self.__ged_options['edit_cost'])) | |||
mge.set_refine_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
self.__mge = MedianGraphEstimator(ged_env, constant_node_costs(self.__ged_options['edit_cost'])) | |||
self.__mge.set_refine_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
options = self.__mge_options.copy() | |||
if not 'seed' in options: | |||
options['seed'] = int(round(time.time() * 1000)) # @todo: may not work correctly for possible parallel usage. | |||
# Select the GED algorithm. | |||
mge.set_options(mge_options_to_string(options)) | |||
mge.set_label_names(node_labels=self._dataset.node_labels, | |||
self.__mge.set_options(mge_options_to_string(options)) | |||
self.__mge.set_label_names(node_labels=self._dataset.node_labels, | |||
edge_labels=self._dataset.edge_labels, | |||
node_attrs=self._dataset.node_attrs, | |||
edge_attrs=self._dataset.edge_attrs) | |||
mge.set_init_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
mge.set_descent_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
self.__mge.set_init_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
self.__mge.set_descent_method(self.__ged_options['method'], ged_options_to_string(self.__ged_options)) | |||
# Run the estimator. | |||
mge.run(graph_ids, set_median_id, gen_median_id) | |||
self.__mge.run(graph_ids, set_median_id, gen_median_id) | |||
# Get SODs. | |||
self.__sod_set_median = mge.get_sum_of_distances('initialized') | |||
self.__sod_gen_median = mge.get_sum_of_distances('converged') | |||
self.__sod_set_median = self.__mge.get_sum_of_distances('initialized') | |||
self.__sod_gen_median = self.__mge.get_sum_of_distances('converged') | |||
# Get median graphs. | |||
self.__set_median = ged_env.get_nx_graph(set_median_id) | |||
@@ -58,6 +58,9 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
itrs_list = [] | |||
converged_list = [] | |||
num_updates_ecc_list = [] | |||
mge_decrease_order_list = [] | |||
mge_increase_order_list = [] | |||
mge_converged_order_list = [] | |||
nb_sod_sm2gm = [0, 0, 0] | |||
nb_dis_k_sm2gm = [0, 0, 0] | |||
nb_dis_k_gi2sm = [0, 0, 0] | |||
@@ -149,7 +152,10 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
results['runtime_precompute_gm'], results['runtime_optimize_ec'], | |||
results['runtime_generate_preimage'], results['runtime_total'], | |||
results['itrs'], results['converged'], | |||
results['num_updates_ecc']]) | |||
results['num_updates_ecc'], | |||
results['mge']['num_decrease_order'] > 0, # @todo: not suitable for multi-start mge | |||
results['mge']['num_increase_order'] > 0, | |||
results['mge']['num_converged_descents'] > 0]) | |||
f_detail.close() | |||
# compute result summary. | |||
@@ -165,6 +171,9 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
itrs_list.append(results['itrs']) | |||
converged_list.append(results['converged']) | |||
num_updates_ecc_list.append(results['num_updates_ecc']) | |||
mge_decrease_order_list.append(results['mge']['num_decrease_order'] > 0) | |||
mge_increase_order_list.append(results['mge']['num_increase_order'] > 0) | |||
mge_converged_order_list.append(results['mge']['num_converged_descents'] > 0) | |||
# # SOD SM -> GM | |||
if results['sod_set_median'] > results['sod_gen_median']: | |||
nb_sod_sm2gm[0] += 1 | |||
@@ -211,7 +220,11 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
results['runtime_precompute_gm'], results['runtime_optimize_ec'], | |||
results['runtime_generate_preimage'], results['runtime_total'], | |||
results['itrs'], results['converged'], | |||
results['num_updates_ecc'], nb_sod_sm2gm, | |||
results['num_updates_ecc'], | |||
results['mge']['num_decrease_order'] > 0, # @todo: not suitable for multi-start mge | |||
results['mge']['num_increase_order'] > 0, | |||
results['mge']['num_converged_descents'] > 0, | |||
nb_sod_sm2gm, | |||
nb_dis_k_sm2gm, nb_dis_k_gi2sm, nb_dis_k_gi2gm]) | |||
f_summary.close() | |||
@@ -257,6 +270,9 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
itrs_mean = np.mean(itrs_list) | |||
num_converged = np.sum(converged_list) | |||
num_updates_ecc_mean = np.mean(num_updates_ecc_list) | |||
num_mge_decrease_order = np.sum(mge_decrease_order_list) | |||
num_mge_increase_order = np.sum(mge_increase_order_list) | |||
num_mge_converged = np.sum(mge_converged_order_list) | |||
sod_sm2gm_mean = get_relations(np.sign(sod_gm_mean - sod_sm_mean)) | |||
dis_k_sm2gm_mean = get_relations(np.sign(dis_k_gm_mean - dis_k_sm_mean)) | |||
dis_k_gi2sm_mean = get_relations(np.sign(dis_k_sm_mean - dis_k_gi_min_mean)) | |||
@@ -271,7 +287,9 @@ def generate_median_preimages_by_class(ds_name, mpg_options, kernel_options, ged | |||
dis_k_gi2sm_mean, dis_k_gi2gm_mean, | |||
time_precompute_gm_mean, time_optimize_ec_mean, | |||
time_generate_mean, time_total_mean, itrs_mean, | |||
num_converged, num_updates_ecc_mean]) | |||
num_converged, num_updates_ecc_mean, | |||
num_mge_decrease_order, num_mge_increase_order, | |||
num_mge_converged]) | |||
f_summary.close() | |||
# save total pairwise kernel distances. | |||
@@ -301,7 +319,8 @@ def __init_output_file(ds_name, gkernel, fit_method, dir_output): | |||
'min dis_k gi', 'SOD SM -> GM', 'dis_k SM -> GM', 'dis_k gi -> SM', | |||
'dis_k gi -> GM', 'edit cost constants', 'time precompute gm', | |||
'time optimize ec', 'time generate preimage', 'time total', | |||
'itrs', 'converged', 'num updates ecc']) | |||
'itrs', 'converged', 'num updates ecc', 'mge decrease order', | |||
'mge increase order', 'mge converged']) | |||
f_detail.close() | |||
# fn_output_summary = 'results_summary.' + ds_name + '.' + gkernel + '.' + fit_method + '.csv' | |||
@@ -313,7 +332,8 @@ def __init_output_file(ds_name, gkernel, fit_method, dir_output): | |||
'min dis_k gi', 'SOD SM -> GM', 'dis_k SM -> GM', 'dis_k gi -> SM', | |||
'dis_k gi -> GM', 'time precompute gm', 'time optimize ec', | |||
'time generate preimage', 'time total', 'itrs', 'num converged', | |||
'num updates ecc', '# SOD SM -> GM', '# dis_k SM -> GM', | |||
'num updates ecc', 'mge num decrease order', 'mge num increase order', | |||
'mge num converged', '# SOD SM -> GM', '# dis_k SM -> GM', | |||
'# dis_k gi -> SM', '# dis_k gi -> GM']) | |||
# 'repeats better SOD SM -> GM', | |||
# 'repeats better dis_k SM -> GM', 'repeats better dis_k gi -> SM', | |||