import torch
from torch_geometric.nn import knn
from torch_scatter import scatter_add
[docs]def knn_interpolate(x, pos_x, pos_y, batch_x=None, batch_y=None, k=3):
r"""The k-NN interpolation from the `"PointNet++: Deep Hierarchical
Feature Learning on Point Sets in a Metric Space"
<https://arxiv.org/abs/1706.02413>`_ paper.
For each point :math:`y` with position :math:`\mathbf{p}(y)`, its
interpolated features :math:`\mathbf{f}(y)` are given by
.. math::
\mathbf{f}(y) = \frac{\sum_{i=1}^k w(x_i) \mathbf{f}(x_i)}{\sum_{i=1}^k
w(x_i)} \textrm{, where } w(x_i) = \frac{1}{d(\mathbf{p}(y),
\mathbf{p}(x_i))^2}
and :math:`\{ x_1, \ldots, x_k \}` denoting the :math:`k` nearest points
to :math:`y`.
Args:
x (Tensor): Node feature matrix
:math:`\mathbf{X} \in \mathbb{R}^{N \times F}`.
pos_x (Tensor): Node position matrix
:math:`\in \mathbb{R}^{N \times d}`.
pos_y (Tensor): Upsampled node position matrix
:math:`\in \mathbb{R}^{M \times d}`.
batch_x (LongTensor, optional): Batch vector
:math:`\mathbf{b_x} \in {\{ 0, \ldots, B-1\}}^N`, which assigns
each node from :math:`\mathbf{X}` to a specific example.
(default: :obj:`None`)
batch_y (LongTensor, optional): Batch vector
:math:`\mathbf{b_y} \in {\{ 0, \ldots, B-1\}}^N`, which assigns
each node from :math:`\mathbf{Y}` to a specific example.
(default: :obj:`None`)
k (int, optional): Number of neighbors. (default: :obj:`3`)
"""
with torch.no_grad():
assign_index = knn(pos_x, pos_y, k, batch_x=batch_x, batch_y=batch_y)
y_idx, x_idx = assign_index
diff = pos_x[x_idx] - pos_y[y_idx]
squared_distance = (diff * diff).sum(dim=-1, keepdim=True)
weights = 1.0 / torch.clamp(squared_distance, min=1e-16)
y = scatter_add(x[x_idx] * weights, y_idx, dim=0, dim_size=pos_y.size(0))
y = y / scatter_add(weights, y_idx, dim=0, dim_size=pos_y.size(0))
return y