text: import torch
  from torch_geometric.nn.conv import MessagePassing
  from torch_cluster import knn_graph
  
  from ..inits import reset
  
  
  class EdgeConv(MessagePassing):
      r"""The edge convolutional operator from the `"Dynamic Graph CNN for
      Learning on Point Clouds" <https://arxiv.org/abs/1801.07829>`_ paper
  
      .. math::
          \mathbf{x}^{\prime}_i = \sum_{j \in \mathcal{N}(i)}
          h_{\mathbf{\Theta}}(\mathbf{x}_i \, \Vert \,
          \mathbf{x}_j - \mathbf{x}_i),
  
      where :math:`h_{\mathbf{\Theta}}` denotes a neural network, *.i.e.* a MLP.
  
      Args:
          nn (torch.nn.Module): A neural network :math:`h_{\mathbf{\Theta}}` that
              maps pair-wise concatenated node features :obj:`x` of shape
              :obj:`[-1, 2 * in_channels]` to shape :obj:`[-1, out_channels]`,
              *e.g.*, defined by :class:`torch.nn.Sequential`.
          aggr (string, optional): The aggregation scheme to use
              (:obj:`"add"`, :obj:`"mean"`, :obj:`"max"`).
              (default: :obj:`"max"`)
          **kwargs (optional): Additional arguments of
              :class:`torch_geometric.nn.conv.MessagePassing`.
      """
  
      def __init__(self, nn, aggr='max', **kwargs):
          super(EdgeConv, self).__init__(aggr='max', **kwargs)
          self.nn = nn
          self.reset_parameters()
  
      def reset_parameters(self):
          reset(self.nn)
  
      def forward(self, x, edge_index):
          """"""
          row, col = edge_index
          x = x.unsqueeze(-1) if x.dim() == 1 else x
  
          return self.propagate(edge_index, x=x)
  
      def message(self, x_i, x_j):
          return self.nn(torch.cat([x_i, x_j - x_i], dim=1))
  
      def __repr__(self):
          return '{}(nn={})'.format(self.__class__.__name__, self.nn)
  
  
  class DynamicEdgeConv(EdgeConv):
      r"""The dynamic edge convolutional operator from the `"Dynamic Graph CNN
      for Learning on Point Clouds" <https://arxiv.org/abs/1801.07829>`_ paper
      (see :class:`torch_geometric.nn.conv.EdgeConv`), where the graph is
      dynamically constructed using nearest neighbors in the feature space.
  
      Args:
          nn (torch.nn.Module): A neural network :math:`h_{\mathbf{\Theta}}` that
              maps pair-wise concatenated node features :obj:`x` of shape
              `:obj:`[-1, 2 * in_channels]` to shape :obj:`[-1, out_channels]`,
              *e.g.* defined by :class:`torch.nn.Sequential`.
          k (int): Number of nearest neighbors.
          aggr (string): The aggregation operator to use (:obj:`"add"`,
              :obj:`"mean"`, :obj:`"max"`). (default: :obj:`"max"`)
          **kwargs (optional): Additional arguments of
              :class:`torch_geometric.nn.conv.MessagePassing`.
      """
  
      def __init__(self, nn, k, aggr='max', **kwargs):
          super(DynamicEdgeConv, self).__init__(nn=nn, aggr=aggr, **kwargs)
          self.k = k
  
      def forward(self, x, batch=None):
          """"""
          edge_index = knn_graph(x, self.k, batch, loop=False, flow=self.flow)
          return super(DynamicEdgeConv, self).forward(x, edge_index)
  
      def __repr__(self):
          return '{}(nn={}, k={})'.format(self.__class__.__name__, self.nn,
                                          self.k)
  