Browse Source

Add class MedianGraphEstimatorPy and MedianPreimageGeneratorPy which use GEDEnv implemented in pure Python.

v0.2.x
jajupmochi 5 years ago
parent
commit
d97bfe954c
6 changed files with 3117 additions and 3 deletions
  1. +41
    -0
      gklearn/ged/env/ged_data.py
  2. +327
    -2
      gklearn/ged/env/ged_env.py
  3. +1
    -0
      gklearn/ged/median/__init__.py
  4. +1711
    -0
      gklearn/ged/median/median_graph_estimator_py.py
  5. +2
    -1
      gklearn/preimage/__init__.py
  6. +1035
    -0
      gklearn/preimage/median_preimage_generator_py.py

+ 41
- 0
gklearn/ged/env/ged_data.py View File

@@ -41,6 +41,17 @@ class GEDData(object):
return len(self._graphs)
def graph(self, graph_id):
"""
/*!
* @brief Provides access to a graph.
* @param[in] graph_id The ID of the graph.
* @return Constant reference to the graph with ID @p graph_id.
*/
"""
return self._graphs[graph_id]
def shuffled_graph_copies_available(self):
"""
/*!
@@ -51,6 +62,16 @@ class GEDData(object):
return (self._init_type == Options.InitType.EAGER_WITH_SHUFFLED_COPIES or self._init_type == Options.InitType.LAZY_WITH_SHUFFLED_COPIES)
def num_graphs_without_shuffled_copies(self):
"""
/*!
* @brief Returns the number of graphs in the instance without the shuffled copies.
* @return Number of graphs without shuffled copies contained in the instance.
*/
"""
return self._num_graphs_without_shuffled_copies
def node_cost(self, label1, label2):
"""
/*!
@@ -177,5 +198,25 @@ class GEDData(object):
self._delete_edit_cost = True
def _node_label_to_id(self, node_label):
n_id = 0
for n_l in self._node_labels:
if n_l == node_label:
return n_id + 1
n_id += 1
self._node_labels.append(node_label)
return n_id + 1


def _edge_label_to_id(self, edge_label):
e_id = 0
for e_l in self._edge_labels:
if e_l == edge_label:
return e_id + 1
e_id += 1
self._edge_labels.append(edge_label)
return e_id + 1
def _eager_init(self):
return (self._init_type == Options.InitType.EAGER_WITHOUT_SHUFFLED_COPIES or self._init_type == Options.InitType.EAGER_WITH_SHUFFLED_COPIES)

+ 327
- 2
gklearn/ged/env/ged_env.py View File

@@ -63,6 +63,23 @@ class GEDEnv(object):
return graph_id
def clear_graph(self, graph_id):
"""
/*!
* @brief Clears and de-initializes a graph that has previously been added to the environment. Call init() after calling this method.
* @param[in] graph_id ID of graph that has to be cleared.
*/
"""
if graph_id > self.__ged_data.num_graphs_without_shuffled_copies():
raise Exception('The graph', self.get_graph_name(graph_id), 'has not been added to the environment.')
self.__ged_data._graphs[graph_id].clear()
self.__original_to_internal_node_ids[graph_id].clear()
self.__internal_to_original_node_ids[graph_id].clear()
self.__ged_data._strings_to_internal_node_ids[graph_id].clear()
self.__ged_data._internal_node_ids_to_strings[graph_id].clear()
self.__initialized = False
def add_node(self, graph_id, node_id, node_label):
"""
/*!
@@ -80,7 +97,9 @@ class GEDEnv(object):
self.__internal_to_original_node_ids[graph_id][internal_node_id] = node_id
self.__ged_data._strings_to_internal_node_ids[graph_id][str(node_id)] = internal_node_id
self.__ged_data._internal_node_ids_to_strings[graph_id][internal_node_id] = str(node_id)
# @todo: node_label_to_id_
self.__ged_data._node_label_to_id(node_label)
label_id = self.__ged_data._node_label_to_id(node_label)
# @todo: ged_data_.graphs_[graph_id].set_label
def add_edge(self, graph_id, nd_from, nd_to, edge_label, ignore_duplicates=True):
@@ -98,7 +117,8 @@ class GEDEnv(object):
self.__initialized = False
# @todo: check ignore_duplicates.
self.__ged_data._graphs[graph_id].add_edge(self.__original_to_internal_node_ids[graph_id][nd_from], self.__original_to_internal_node_ids[graph_id][nd_to], label=edge_label)
# @todo: edge_id and label_id, edge_label_to_id_.
label_id = self.__ged_data._edge_label_to_id(edge_label)
# @todo: ged_data_.graphs_[graph_id].set_label
def add_nx_graph(self, g, classe, ignore_duplicates=True) :
@@ -123,6 +143,40 @@ class GEDEnv(object):
return graph_id
def load_nx_graph(self, nx_graph, graph_id, graph_name='', graph_class=''):
"""
Loads NetworkX Graph into the GED environment.

Parameters
----------
nx_graph : NetworkX Graph object
The graph that should be loaded.
graph_id : int or None
The ID of a graph contained the environment (overwrite existing graph) or add new graph if `None`.
graph_name : string, optional
The name of newly added graph. The default is ''. Has no effect unless `graph_id` equals `None`.
graph_class : string, optional
The class of newly added graph. The default is ''. Has no effect unless `graph_id` equals `None`.

Returns
-------
int
The ID of the newly loaded graph.
"""
if graph_id is None: # @todo: undefined.
graph_id = self.add_graph(graph_name, graph_class)
else:
self.clear_graph(graph_id)
for node in nx_graph.nodes:
self.add_node(graph_id, node, tuple(sorted(nx_graph.nodes[node].items(), key=lambda kv: kv[0])))
for edge in nx_graph.edges:
self.add_edge(graph_id, edge[0], edge[1], tuple(sorted(nx_graph.edges[(edge[0], edge[1])].items(), key=lambda kv: kv[0])))
return graph_id
def init(self, init_type=Options.InitType.EAGER_WITHOUT_SHUFFLED_COPIES, print_to_stdout=False):
if isinstance(init_type, str):
init_type = OptionsStringMap.InitType[init_type]
@@ -154,6 +208,26 @@ class GEDEnv(object):
self.__new_graph_ids.clear()
def is_initialized(self):
"""
/*!
* @brief Check if the environment is initialized.
* @return True if the environment is initialized.
*/
"""
return self.__initialized
def get_init_type(self):
"""
/*!
* @brief Returns the initialization type of the last initialization.
* @return Initialization type.
*/
"""
return self.__ged_data._init_type
def set_method(self, method, options=''):
"""
/*!
@@ -263,6 +337,58 @@ class GEDEnv(object):
self.__ged_method.init()
def get_num_node_labels(self):
"""
/*!
* @brief Returns the number of node labels.
* @return Number of pairwise different node labels contained in the environment.
* @note If @p 1 is returned, the nodes are unlabeled.
*/
"""
return len(self.__ged_data._node_labels)
def get_node_label(self, label_id, to_dict=True):
"""
/*!
* @brief Returns node label.
* @param[in] label_id ID of node label that should be returned. Must be between 1 and num_node_labels().
* @return Node label for selected label ID.
*/
"""
if label_id < 1 or label_id > self.get_num_node_labels():
raise Exception('The environment does not contain a node label with ID', str(label_id), '.')
if to_dict:
return dict(self.__ged_data._node_labels[label_id - 1])
return self.__ged_data._node_labels[label_id - 1]
def get_num_edge_labels(self):
"""
/*!
* @brief Returns the number of edge labels.
* @return Number of pairwise different edge labels contained in the environment.
* @note If @p 1 is returned, the edges are unlabeled.
*/
"""
return len(self.__ged_data._edge_labels)
def get_edge_label(self, label_id, to_dict=True):
"""
/*!
* @brief Returns edge label.
* @param[in] label_id ID of edge label that should be returned. Must be between 1 and num_node_labels().
* @return Edge label for selected label ID.
*/
"""
if label_id < 1 or label_id > self.get_num_edge_labels():
raise Exception('The environment does not contain an edge label with ID', str(label_id), '.')
if to_dict:
return dict(self.__ged_data._edge_labels[label_id - 1])
return self.__ged_data._edge_labels[label_id - 1]
def get_upper_bound(self, g_id, h_id):
"""
/*!
@@ -363,6 +489,205 @@ class GEDEnv(object):
.. note:: I don't know how to connect the two map to reconstruct the adjacence matrix. Please come back when I know how it's work !
"""
return self.get_node_map(g_id, h_id).backward_map
def compute_induced_cost(self, g_id, h_id, 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] h_id ID of input graph.
* @param[in,out] node_map Node map whose induced edit cost is to be computed.
*/
"""
self.__ged_data.compute_induced_cost(self.__ged_data._graphs[g_id], self.__ged_data._graphs[h_id], node_map)
def get_nx_graph(self, graph_id):
"""
* @brief Returns NetworkX.Graph() representation.
* @param[in] graph_id ID of the selected graph.
"""
graph = nx.Graph() # @todo: add graph attributes.
graph.graph['id'] = graph_id
nb_nodes = self.get_graph_num_nodes(graph_id)
original_node_ids = self.get_original_node_ids(graph_id)
node_labels = self.get_graph_node_labels(graph_id, to_dict=True)
graph.graph['original_node_ids'] = original_node_ids
for node_id in range(0, nb_nodes):
graph.add_node(node_id, **node_labels[node_id])
edges = self.get_graph_edges(graph_id, to_dict=True)
for (head, tail), labels in edges.items():
graph.add_edge(head, tail, **labels)

return graph
def get_graph_node_labels(self, graph_id, to_dict=True):
"""
Searchs and returns all the labels of nodes on a graph, selected by its ID.
:param graph_id: The ID of the wanted graph
:type graph_id: size_t
:return: The list of nodes' labels on the selected graph
:rtype: list[dict{string : string}]
.. seealso:: get_graph_internal_id(), get_graph_num_nodes(), get_graph_num_edges(), get_original_node_ids(), get_graph_edges(), get_graph_adjacence_matrix()
.. note:: These functions allow to collect all the graph's informations.
"""
graph = self.__ged_data.graph(graph_id)
node_labels = []
for n in graph.nodes():
node_labels.append(graph.nodes[n]['label'])
if to_dict:
return [dict(i) for i in node_labels]
return node_labels
def get_graph_edges(self, graph_id, to_dict=True):
"""
Searchs and returns all the edges on a graph, selected by its ID.
:param graph_id: The ID of the wanted graph
:type graph_id: size_t
:return: The list of edges on the selected graph
:rtype: dict{tuple(size_t, size_t) : dict{string : string}}
.. seealso::get_graph_internal_id(), get_graph_num_nodes(), get_graph_num_edges(), get_original_node_ids(), get_graph_node_labels(), get_graph_adjacence_matrix()
.. note:: These functions allow to collect all the graph's informations.
"""
graph = self.__ged_data.graph(graph_id)
if to_dict:
edges = {}
for n1, n2, attr in graph.edges(data=True):
edges[(n1, n2)] = dict(attr['label'])
return edges
return {(n1, n2): attr['label'] for n1, n2, attr in graph.edges(data=True)}

def get_graph_name(self, graph_id):
"""
/*!
* @brief Returns the graph name.
* @param[in] graph_id ID of an input graph that has been added to the environment.
* @return Name of the input graph.
*/
"""
return self.__ged_data._graph_names[graph_id]
def get_graph_num_nodes(self, graph_id):
"""
/*!
* @brief Returns the number of nodes.
* @param[in] graph_id ID of an input graph that has been added to the environment.
* @return Number of nodes in the graph.
*/
"""
return nx.number_of_nodes(self.__ged_data.graph(graph_id))
def get_original_node_ids(self, graph_id):
"""
Searchs and returns all th Ids of nodes on a graph, selected by its ID.
:param graph_id: The ID of the wanted graph
:type graph_id: size_t
:return: The list of IDs's nodes on the selected graph
:rtype: list[string]
.. seealso::get_graph_internal_id(), get_graph_num_nodes(), get_graph_num_edges(), get_graph_node_labels(), get_graph_edges(), get_graph_adjacence_matrix()
.. note:: These functions allow to collect all the graph's informations.
"""
return [i for i in self.__internal_to_original_node_ids[graph_id].values()]
def get_node_rel_cost(self, node_label_1, node_label_2):
"""
/*!
* @brief Returns node relabeling cost.
* @param[in] node_label_1 First node label.
* @param[in] node_label_2 Second node label.
* @return Node relabeling cost for the given node labels.
*/
"""
if isinstance(node_label_1, dict):
node_label_1 = tuple(sorted(node_label_1.items(), key=lambda kv: kv[0]))
if isinstance(node_label_2, dict):
node_label_2 = tuple(sorted(node_label_2.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.node_rel_cost_fun(node_label_1, node_label_2)
def get_node_del_cost(self, node_label):
"""
/*!
* @brief Returns node deletion cost.
* @param[in] node_label Node label.
* @return Cost of deleting node with given label.
*/
"""
if isinstance(node_label, dict):
node_label = tuple(sorted(node_label.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.node_del_cost_fun(node_label)
def get_node_ins_cost(self, node_label):
"""
/*!
* @brief Returns node insertion cost.
* @param[in] node_label Node label.
* @return Cost of inserting node with given label.
*/
"""
if isinstance(node_label, dict):
node_label = tuple(sorted(node_label.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.node_ins_cost_fun(node_label)
def get_edge_rel_cost(self, edge_label_1, edge_label_2):
"""
/*!
* @brief Returns edge relabeling cost.
* @param[in] edge_label_1 First edge label.
* @param[in] edge_label_2 Second edge label.
* @return Edge relabeling cost for the given edge labels.
*/
"""
if isinstance(edge_label_1, dict):
edge_label_1 = tuple(sorted(edge_label_1.items(), key=lambda kv: kv[0]))
if isinstance(edge_label_2, dict):
edge_label_2 = tuple(sorted(edge_label_2.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.edge_rel_cost_fun(edge_label_1, edge_label_2)
def get_edge_del_cost(self, edge_label):
"""
/*!
* @brief Returns edge deletion cost.
* @param[in] edge_label Edge label.
* @return Cost of deleting edge with given label.
*/
"""
if isinstance(edge_label, dict):
edge_label = tuple(sorted(edge_label.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.edge_del_cost_fun(edge_label)
def get_edge_ins_cost(self, edge_label):
"""
/*!
* @brief Returns edge insertion cost.
* @param[in] edge_label Edge label.
* @return Cost of inserting edge with given label.
*/
"""
if isinstance(edge_label, dict):
edge_label = tuple(sorted(edge_label.items(), key=lambda kv: kv[0]))
return self.__ged_data._edit_cost.edge_ins_cost_fun(edge_label)
def get_all_graph_ids(self):

+ 1
- 0
gklearn/ged/median/__init__.py View File

@@ -1,2 +1,3 @@
from gklearn.ged.median.median_graph_estimator import MedianGraphEstimator
from gklearn.ged.median.median_graph_estimator_py import MedianGraphEstimatorPy
from gklearn.ged.median.utils import constant_node_costs, mge_options_to_string

+ 1711
- 0
gklearn/ged/median/median_graph_estimator_py.py
File diff suppressed because it is too large
View File


+ 2
- 1
gklearn/preimage/__init__.py View File

@@ -11,8 +11,9 @@ __author__ = "Linlin Jia"
__date__ = "March 2020"

from gklearn.preimage.preimage_generator import PreimageGenerator
from gklearn.preimage.median_preimage_generator import MedianPreimageGenerator
from gklearn.preimage.random_preimage_generator import RandomPreimageGenerator
from gklearn.preimage.median_preimage_generator import MedianPreimageGenerator
from gklearn.preimage.median_preimage_generator_py import MedianPreimageGeneratorPy
from gklearn.preimage.median_preimage_generator_cml import MedianPreimageGeneratorCML
from gklearn.preimage.kernel_knn_cv import kernel_knn_cv
from gklearn.preimage.generate_random_preimages_by_class import generate_random_preimages_by_class

+ 1035
- 0
gklearn/preimage/median_preimage_generator_py.py
File diff suppressed because it is too large
View File


Loading…
Cancel
Save