def convert_ensmallen_graph_to_networkx_graph(graph: Graph) -> nx.Graph: """Return networkX graph derived from the provided Ensmallen Graph. Parameters ----------- graph: Graph The graph to be converted. """ if graph.is_directed(): result_graph = nx.DiGraph() else: result_graph = nx.Graph() result_graph.add_nodes_from(graph.get_node_ids()) if graph.has_edge_weights(): result_graph.add_weighted_edges_from([ (src_name, dst_name, edge_weight) for (src_name, dst_name), edge_weight in zip( graph.get_directed_edge_node_ids(), graph.get_edge_weights()) ]) else: result_graph.add_edges_from( graph.get_edge_node_ids(directed=graph.is_directed())) return result_graph
def graph_to_sparse_tensor( graph: Graph, use_weights: bool, use_simmetric_normalized_laplacian: bool, handling_multi_graph: str = "warn", ) -> tf.SparseTensor: """Returns provided graph as sparse Tensor. Parameters ------------------- graph: Graph, The graph to convert. use_weights: bool, Whether to load the graph weights. use_simmetric_normalized_laplacian: bool Whether to use the symmetrically normalized laplacian handling_multi_graph: str = "warn" How to behave when dealing with multigraphs. Possible behaviours are: - "warn", which warns the user and drops the multi-edges. - "raise" - "drop" Raises ------------------- ValueError, If the weights are requested but the graph does not contain any. ValueError, If the graph contains singletons. ValueError, If the graph is a multigraph. Returns ------------------- SparseTensor with (weighted) adjacency matrix. """ if use_weights and not graph.has_edge_weights(): raise ValueError("Edge weights were requested but the provided graph " "does not contain any edge weight.") if graph.has_singleton_nodes(): raise ValueError( f"In the provided {graph.get_name()} graph there are " f"{graph.get_number_of_singleton_nodes()} singleton nodes." "The GCN model does not support operations on graph containing " "singletons. You can either choose to drop singletons from " "the graph by using the `graph.remove_singleton_nodes()` " "method or alternatively you can add selfloops to them by " "using the `graph.add_selfloops()` method.") if graph.is_multigraph(): message = ( "The GCN model does not currently support convolutions on a multigraph. " "We are dropping the parallel edges before computing the adjacency matrix." ) if handling_multi_graph == "warn": warnings.warn(message) elif handling_multi_graph == "raise": raise ValueError(message) graph = graph.remove_parallel_edges() if use_simmetric_normalized_laplacian: edge_node_ids, weights = graph.get_symmetric_normalized_laplacian_coo_matrix( ) return tf.sparse.reorder( tf.SparseTensor( edge_node_ids, np.abs(weights), (graph.get_number_of_nodes(), graph.get_number_of_nodes()))) return tf.SparseTensor( graph.get_directed_edge_node_ids(), (graph.get_edge_weights() if use_weights else tf.ones(graph.get_number_of_directed_edges())), (graph.get_number_of_nodes(), graph.get_number_of_nodes()))
def _fit_transform(self, graph: Graph, return_dataframe: bool = True, verbose: bool = True) -> EmbeddingResult: """Return node embedding.""" matrix = None if self._metric == "Jaccard": edges, weights = graph.get_jaccard_coo_matrix() elif self._metric == "Laplacian": edges, weights = graph.get_laplacian_coo_matrix() elif self._metric == "Modularity": matrix = graph.get_dense_modularity_matrix() elif self._metric == "Left Normalized Laplacian": edges, weights = graph.get_left_normalized_laplacian_coo_matrix() elif self._metric == "Right Normalized Laplacian": edges, weights = graph.get_right_normalized_laplacian_coo_matrix() elif self._metric == "Symmetric Normalized Laplacian": edges, weights = graph.get_symmetric_normalized_laplacian_coo_matrix( ) elif self._metric == "Neighbours Intersection size": edges, weights = graph.get_neighbours_intersection_size_coo_matrix( ) elif self._metric == "Ancestors Jaccard": matrix = graph.get_shared_ancestors_jaccard_adjacency_matrix( graph.get_breadth_first_search_from_node_names( src_node_name=self._root_node_name, compute_predecessors=True), verbose=verbose) elif self._metric == "Ancestors size": matrix = graph.get_shared_ancestors_size_adjacency_matrix( graph.get_breadth_first_search_from_node_names( src_node_name=self._root_node_name, compute_predecessors=True), verbose=verbose) elif self._metric == "Adamic-Adar": edges, weights = graph.get_adamic_adar_coo_matrix() elif self._metric == "Adjacency": edges, weights = graph.get_directed_edge_node_ids(), np.ones( graph.get_number_of_directed_edges()) else: raise NotImplementedError(f"The provided metric {self._metric} " "is not currently supported.") if matrix is None: matrix = coo_matrix((weights, (edges[:, 0], edges[:, 1])), shape=(graph.get_number_of_nodes(), graph.get_number_of_nodes()), dtype=np.float32) U, sigmas, Vt = sparse_svds(matrix, k=int(self._embedding_size / 2)) else: U, sigmas, Vt = randomized_svd(matrix, n_components=int( self._embedding_size / 2)) sigmas = np.diagflat(np.sqrt(sigmas)) left_embedding = np.dot(U, sigmas) right_embedding = np.dot(Vt.T, sigmas) if return_dataframe: node_names = graph.get_node_names() left_embedding = pd.DataFrame(left_embedding, index=node_names) right_embedding = pd.DataFrame(right_embedding, index=node_names) return EmbeddingResult( embedding_method_name=self.model_name(), node_embeddings=[left_embedding, right_embedding])