//  [python]
def forward_single(self, x, idx, eval=False):
        kernel_feat = x
        kernel_feat_conv_comb = x
        cate_feat = x
        # kernel branch
        # concat coord

        x_range = torch.linspace(-1, 1, kernel_feat.shape[-1], device=kernel_feat.device)
        y_range = torch.linspace(-1, 1, kernel_feat.shape[-2], device=kernel_feat.device)
        y, x = torch.meshgrid(y_range, x_range)
        y = y.expand([kernel_feat.shape[0], 1, -1, -1])
        x = x.expand([kernel_feat.shape[0], 1, -1, -1])
        coord_feat = torch.cat([x, y], 1)

        kernel_feat = torch.cat([kernel_feat, coord_feat], 1)
        for i, kernel_layer in enumerate(self.kernel_convs):
            if i == self.cate_down_pos:
                mask_num_grid = self.mask_grid_number[idx]
                kernel_feat = F.interpolate(kernel_feat, size=mask_num_grid, mode='bilinear')
            kernel_feat = kernel_layer(kernel_feat)
        kernel_pred = self.solo_kernel(kernel_feat)

        # cate branch
        for i, cate_layer in enumerate(self.cate_convs):
            if i == self.cate_down_pos:
                cls_num_grid = self.cls_grid_number[idx]
                cate_feat = F.interpolate(cate_feat, size=cls_num_grid, mode='bilinear')
            cate_feat = cate_layer(cate_feat)

        ##seperate branch for predicting conv comb weights
        if self.sep_comb_lw_branch:
            kernel_feat = torch.cat([kernel_feat_conv_comb, coord_feat], 1)
            for i, kernel_layer in enumerate(self.kernel_convs_convcomb):
                if i == self.cate_down_pos:
                    cls_num_grid = self.cls_grid_number[idx]
                    kernel_feat = F.interpolate(kernel_feat, size=cls_num_grid, mode='bilinear')
                kernel_feat = kernel_layer(kernel_feat)
            learned_upsample_weight = self.learned_weights(kernel_feat)
        else:
            learned_upsample_weight = self.learned_weights(kernel_feat)
        ##add spatial index
        l = idx
        shifts_x = torch.arange(
            0, self.cls_grid_number[l], step=1,
            dtype=torch.float32, device=cate_feat.device
        )
        shifts_y = torch.arange(
            0, self.cls_grid_number[l], step=1,
            dtype=torch.float32, device=cate_feat.device
        )
        shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x)
        # locations_HW = torch.stack([shift_x, shift_y],dim=2)## transposed index
        locations_HW = torch.stack([shift_y, shift_x], dim=2)  ## correct index
        locations_HW *= ((self.mask_grid_number[l] - 1) / (self.cls_grid_number[l] - 1))
        locations_map_to_HW = locations_HW.view(-1, 2)

        if self.nearest_neighbour == 4 or self.nearest_neighbour == 5 or self.nearest_neighbour == 1 or self.nearest_neighbour == 0:
            if any([clsg == maskg for i, (clsg, maskg) in enumerate(zip(self.cls_grid_number, self.mask_grid_number))]) or self.interpolation_mode==5:  # if equal, get the near 5 points
                ## find nearest center
                neareast_center = locations_map_to_HW.round()

                ## generating nearest neighbour offsets
                shifts_x = torch.arange(
                    -1, 2, step=1,
                    dtype=torch.float32, device=cate_feat.device
                )
                shifts_y = torch.arange(
                    -1, 2, step=1,
                    dtype=torch.float32, device=cate_feat.device
                )
                shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x)
                locations_offset = torch.stack([shift_y, shift_x], dim=2)
                locations_offset = torch.stack([locations_offset[0, 1],
                                                locations_offset[1, 0],
                                                locations_offset[1, 1],
                                                locations_offset[1, 2],
                                                locations_offset[2, 1]])
                spatial_index_this_lvl = neareast_center.unsqueeze(1) + locations_offset.unsqueeze(0)
                spatial_index_this_lvl = spatial_index_this_lvl.clamp(0, self.mask_grid_number[l] - 1).long()

            else:  # mask_grid_number less than cls grid number
                right_top_inds = torch.stack([locations_map_to_HW[:, 0].floor(), locations_map_to_HW[:, 1].ceil()],
                                             dim=1).long()
                right_bottom_inds = locations_map_to_HW.ceil().long()
                left_top_inds = torch.stack([locations_map_to_HW[:, 0].ceil(), locations_map_to_HW[:, 1].floor()],
                                            dim=1).long()
                left_bottom_inds = locations_map_to_HW.floor().long()

                spatial_index_this_lvl = torch.stack(
                    [right_top_inds, right_bottom_inds, left_top_inds, left_bottom_inds], dim=1)

        elif self.nearest_neighbour == 9:
            ## find nearest center
            neareast_center = locations_map_to_HW.round()
            ## generating nearest neighbour offsets
            shifts_x = torch.arange(
                -1, 2, step=1,
                dtype=torch.float32, device=cate_feat.device
            )
            shifts_y = torch.arange(
                -1, 2, step=1,
                dtype=torch.float32, device=cate_feat.device
            )
            shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x)
            locations_offset = torch.stack([shift_y, shift_x], dim=2).view(9, 2)
            spatial_index_this_lvl = neareast_center.unsqueeze(1) + locations_offset.unsqueeze(0)
            spatial_index_this_lvl = spatial_index_this_lvl.clamp(0, self.mask_grid_number[
                l] - 1).long()  ## clamp for boundary points

        cate_pred = self.solo_cate(cate_feat)

        if eval:
            cate_pred = points_nms(cate_pred.sigmoid(), kernel=2).permute(0, 2, 3, 1)
        return kernel_pred, cate_pred, spatial_index_this_lvl, learned_upsample_weight