Source code for tgp.select.graclus_select

from typing import Optional

import torch
from torch import Tensor
from torch_geometric.utils.num_nodes import maybe_num_nodes

from tgp.imports import check_torch_cluster_available, torch_cluster
from tgp.select import Select, SelectOutput
from tgp.utils import connectivity_to_edge_index
from tgp.utils.typing import SinvType


[docs] class GraclusSelect(Select): r"""The :math:`\texttt{select}` operator inspired by the paper `"Weighted Graph Cuts without Eigenvectors: A Multilevel Approach" <https://ieeexplore.ieee.org/document/4302760>`_ (Dhillon et al., TPAMI 2007). It implements a greedy clustering algorithm for picking an unmarked vertex and matching it with one of its unmarked neighbors (that maximizes its edge weight). Args: s_inv_op (~tgp.utils.typing.SinvType, optional): The operation used to compute :math:`\mathbf{S}_\text{inv}` from the select matrix :math:`\mathbf{S}`. :math:`\mathbf{S}_\text{inv}` is stored in the ``"s_inv"`` attribute of the :class:`~tgp.select.SelectOutput`. It can be one of: - ``"transpose"`` (default): Computes :math:`\mathbf{S}_\text{inv}` as :math:`\mathbf{S}^\top`, the transpose of :math:`\mathbf{S}`. - ``"inverse"``: Computes :math:`\mathbf{S}_\text{inv}` as :math:`\mathbf{S}^+`, the Moore-Penrose pseudoinverse of :math:`\mathbf{S}`. """ def __init__(self, s_inv_op: SinvType = "transpose"): super().__init__() self.s_inv_op = s_inv_op
[docs] def forward( self, edge_index: Tensor, edge_weight: Optional[Tensor] = None, num_nodes: Optional[int] = None, **kwargs, ) -> SelectOutput: r"""Forward pass. Args: edge_index (~torch.Tensor, optional): The edge indices. Is a tensor of of shape :math:`[2, E]`, where :math:`E` is the number of edges in the batch. (default: :obj:`None`) edge_weight (~torch.Tensor, optional): A vector of shape :math:`[E]` or :math:`[E, 1]` containing the weights of the edges. (default: :obj:`None`) num_nodes (int, optional): The total number of nodes of the graphs in the batch. (default: :obj:`None`) Returns: :class:`~tgp.select.SelectOutput`: The output of :math:`\texttt{select}` operator. """ check_torch_cluster_available() edge_index, edge_weight = connectivity_to_edge_index(edge_index, edge_weight) num_nodes = maybe_num_nodes(edge_index, num_nodes) assignment = torch_cluster.graclus_cluster( edge_index[0], edge_index[1], edge_weight, num_nodes ) # relabel nodes ids, assignment = torch.unique(assignment, sorted=True, return_inverse=True) num_supernodes = ids.size(0) so = SelectOutput( node_index=torch.arange(num_nodes, device=assignment.device), num_nodes=num_nodes, cluster_index=assignment, num_supernodes=num_supernodes, s_inv_op=self.s_inv_op, ) return so
def __repr__(self) -> str: return f"{self.__class__.__name__}(s_inv_op={self.s_inv_op})"