code: def backward_gpu(self, inputs, gy):
      _, bottom_rois, bottom_roi_indices = inputs
      channel, height, width = self._bottom_data_shape[1:]
      out_c, out_h, out_w = gy[0].shape[1:]
      bottom_diff = cuda.cupy.zeros(self._bottom_data_shape, np.float32)
  
      if self.sampling_ratio[0] is None:
          sampling_ratio_h = 0
      else:
          sampling_ratio_h = self.sampling_ratio[0]
      if self.sampling_ratio[1] is None:
          sampling_ratio_w = 0
      else:
          sampling_ratio_w = self.sampling_ratio[1]
      cuda.elementwise(
          """
              raw T top_diff, raw int32 argmax_data,
              raw T bottom_rois, raw int32 bottom_roi_indices,
              T spatial_scale, int32 channel, int32 height, int32 width,
              int32 pooled_dim, int32 pooled_height, int32 pooled_width,
              int32 group_size, int32 sampling_ratio_h, int32 sampling_ratio_w
              """,
          "raw T bottom_diff",
          """
              // (n, c, h, w) coords in bottom data
              int pw = i % pooled_width;
              int ph = (i / pooled_width) % pooled_height;
              int ctop = (i / pooled_width / pooled_height) % pooled_dim;
              int n = i / pooled_width / pooled_height / pooled_dim;
  
              // Do not using rounding; this implementation detail is critical
              int roi_batch_ind = bottom_roi_indices[n];
              T roi_start_h = bottom_rois[n * 4 + 0] * spatial_scale;
              T roi_start_w = bottom_rois[n * 4 + 1] * spatial_scale;
              T roi_end_h = bottom_rois[n * 4 + 2] * spatial_scale;
              T roi_end_w = bottom_rois[n * 4 + 3] * spatial_scale;
  
              // Force too small ROIs to be 1x1
              T roi_height = max(roi_end_h - roi_start_h, 0.1);
              T roi_width = max(roi_end_w - roi_start_w, 0.1);  // avoid 0
  
              // Compute w and h at bottom
              T bin_size_h = roi_height / static_cast<T>(pooled_height);
              T bin_size_w = roi_width / static_cast<T>(pooled_width);
  
              // Compute c at bottom
              int gh = floor(
                  static_cast<T>(ph) * group_size / pooled_height);
              int gw = floor(
                  static_cast<T>(pw) * group_size / pooled_width);
              gh = min(max(gh, 0), group_size - 1);
              gw = min(max(gw, 0), group_size - 1);
              int c = (ctop * group_size + gh) * group_size + gw;
  
              int bottom_diff_offset =
                  (roi_batch_ind * channel + c) * height * width;
  
              int top_offset =
                  (n * pooled_dim + ctop) * pooled_height * pooled_width;
              T top_diff_this_bin =
                  top_diff[top_offset + ph * pooled_width + pw];
              int maxidx = argmax_data[top_offset + ph * pooled_width + pw];
  
              if (maxidx != -1) {
                  // We use roi_bin_grid to sample the grid and mimic integral
                  int roi_bin_grid_h = (sampling_ratio_h > 0)
                      ? sampling_ratio_h
                      : ceil(roi_height / pooled_height); // e.g. = 2
                  int roi_bin_grid_w = (sampling_ratio_w > 0)
                      ? sampling_ratio_w
                      : ceil(roi_width / pooled_width);
  
                  int iy = maxidx / roi_bin_grid_w;
                  int ix = maxidx % roi_bin_grid_w;
  
                  T y = roi_start_h + ph * bin_size_h +
                      static_cast<T>(iy + .5f) * bin_size_h /
                          static_cast<T>(roi_bin_grid_h);  // e.g. 0.5, 1.5
                  T x = roi_start_w + pw * bin_size_w +
                      static_cast<T>(ix + .5f) * bin_size_w /
                          static_cast<T>(roi_bin_grid_w);
  
                  int y_low, y_high;
                  bool y_ret = get_bounds(y, height, y_low, y_high);
                  if (!y_ret) continue;
                  int x_low, x_high;
                  bool x_ret = get_bounds(x, width, x_low, x_high);
                  if (!x_ret) continue;
  
                  // bilinear_interpolation_gradient {{
                  T w1, w2, w3, w4;
                  get_bilinear_interp_params(
                      y, x, y_low, x_low, y_high, x_high, w1, w2, w3, w4);
  
                  if (w1 > 0 && y_low >= 0 && x_low >= 0) {
                      T g1 = top_diff_this_bin * w1;
                      atomicAdd(&bottom_diff[
                          bottom_diff_offset + y_low * width + x_low], g1);
                  }
                  if (w2 > 0 && y_low >= 0 && x_high <= width - 1) {
                      T g2 = top_diff_this_bin * w2;
                      atomicAdd(&bottom_diff[
                          bottom_diff_offset + y_low * width + x_high], g2);
                  }
                  if (w3 > 0 && y_high <= height - 1 && x_low >= 0) {
                      T g3 = top_diff_this_bin * w3;
                      atomicAdd(&bottom_diff[
                          bottom_diff_offset + y_high * width + x_low], g3);
                  }
                  if (w4 > 0 && y_high <= height - 1 && x_high <= width - 1) {
                      T g4 = top_diff_this_bin * w4;
                      atomicAdd(&bottom_diff[
                          bottom_diff_offset + y_high * width + x_high], g4);
                  }
  
                  // }}
              }
              """,
          "ps_roi_max_align_2d_bwd",
          preamble=_GET_BILINEAR_INTERP_KERNEL,
      )(
          gy[0],
          self.argmax_data,
          bottom_rois,
          bottom_roi_indices,
          self.spatial_scale,
          channel,
          height,
          width,
          out_c,
          out_h,
          out_w,
          self.group_size,
          sampling_ratio_h,
          sampling_ratio_w,
          bottom_diff,
          size=gy[0].size,
      )
  
      return bottom_diff, None, None
  
imports: from __future__ import division
  import numbers
  import numpy
  import six
  from chainer.backends import cuda
  from chainer import function
  from chainer.utils import type_check
  from chainercv.functions.ps_roi_average_align_2d import _GET_BILINEAR_INTERP_KERNEL
  from chainercv.functions.ps_roi_average_align_2d import _get_bilinear_interp_params
  from chainercv.functions.ps_roi_average_align_2d import _get_bounds
  from chainercv.functions.ps_roi_average_align_2d import _pair
  from chainercv.functions.ps_roi_average_pooling_2d import _outsize
label_desc: Use image node_id 6 for calling the PSROIMaxAlign2D obj's underlying member method code with example usage: obj.backward_gpu(inputs, gy) and returns: bottom_diff,  None,  None