code: def affine_transform(
      input,
      matrix,
      offset=0.0,
      output_shape=None,
      output=None,
      order=3,
      mode="constant",
      cval=0.0,
      prefilter=True,
      *,
      texture_memory=False,
  ):
      """Apply an affine transformation.
  
      Given an output image pixel index vector ``o``, the pixel value is
      determined from the input image at position
      ``cupy.dot(matrix, o) + offset``.
  
      Args:
          input (cupy.ndarray): The input array.
          matrix (cupy.ndarray): The inverse coordinate transformation matrix,
              mapping output coordinates to input coordinates. If ``ndim`` is the
              number of dimensions of ``input``, the given matrix must have one
              of the following shapes:
  
                  - ``(ndim, ndim)``: the linear transformation matrix for each
                    output coordinate.
                  - ``(ndim,)``: assume that the 2D transformation matrix is
                    diagonal, with the diagonal specified by the given value.
                  - ``(ndim + 1, ndim + 1)``: assume that the transformation is
                    specified using homogeneous coordinates. In this case, any
                    value passed to ``offset`` is ignored.
                  - ``(ndim, ndim + 1)``: as above, but the bottom row of a
                    homogeneous transformation matrix is always
                    ``[0, 0, ..., 1]``, and may be omitted.
  
          offset (float or sequence): The offset into the array where the
              transform is applied. If a float, ``offset`` is the same for each
              axis. If a sequence, ``offset`` should contain one value for each
              axis.
          output_shape (tuple of ints): Shape tuple.
          output (cupy.ndarray or ~cupy.dtype): The array in which to place the
              output, or the dtype of the returned array.
          order (int): The order of the spline interpolation, default is 3. Must
              be in the range 0-5.
          mode (str): Points outside the boundaries of the input are filled
              according to the given mode (``'constant'``, ``'nearest'``,
              ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``,
              ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``).
          cval (scalar): Value used for points outside the boundaries of
              the input if ``mode='constant'`` or ``mode='opencv'``. Default is
              0.0
          prefilter (bool): Determines if the input array is prefiltered with
              ``spline_filter`` before interpolation. The default is True, which
              will create a temporary ``float64`` array of filtered values if
              ``order > 1``. If setting this to False, the output will be
              slightly blurred if ``order > 1``, unless the input is prefiltered,
              i.e. it is the result of calling ``spline_filter`` on the original
              input.
          texture_memory (bool): If True, uses GPU texture memory. Supports only:
  
              - 2D and 3D float32 arrays as input
              - ``(ndim + 1, ndim + 1)`` homogeneous float32 transformation
                  matrix
              - ``mode='constant'`` and ``mode='nearest'``
              - ``order=0`` (nearest neighbor) and ``order=1`` (linear
                  interpolation)
              - NVIDIA CUDA GPUs
  
      Returns:
          cupy.ndarray or None:
              The transformed input. If ``output`` is given as a parameter,
              ``None`` is returned.
  
      .. seealso:: :func:`scipy.ndimage.affine_transform`
      """
  
      if texture_memory:
          if runtime.is_hip:
              raise RuntimeError(
                  "HIP currently does not support texture acceleration"
              )
          tm_interp = "linear" if order > 0 else "nearest"
          return _texture.affine_transformation(
              data=input,
              transformation_matrix=matrix,
              output_shape=output_shape,
              output=output,
              interpolation=tm_interp,
              mode=mode,
              border_value=cval,
          )
  
      _check_parameter("affine_transform", order, mode)
  
      offset = _util._fix_sequence_arg(
          offset, input.ndim, "offset", float
      )
  
      if matrix.ndim not in [1, 2] or matrix.shape[0] < 1:
          raise RuntimeError("no proper affine matrix provided")
      if matrix.ndim == 2:
          if matrix.shape[0] == matrix.shape[1] - 1:
              offset = matrix[:, -1]
              matrix = matrix[:, :-1]
          elif matrix.shape[0] == input.ndim + 1:
              offset = matrix[:-1, -1]
              matrix = matrix[:-1, :-1]
          if matrix.shape != (input.ndim, input.ndim):
              raise RuntimeError("improper affine shape")
  
      if mode == "opencv":
          m = cupy.zeros((input.ndim + 1, input.ndim + 1))
          m[:-1, :-1] = matrix
          m[:-1, -1] = offset
          m[-1, -1] = 1
          m = cupy.linalg.inv(m)
          m[:2] = cupy.roll(m[:2], 1, axis=0)
          m[:2, :2] = cupy.roll(m[:2, :2], 1, axis=1)
          matrix = m[:-1, :-1]
          offset = m[:-1, -1]
  
      if output_shape is None:
          output_shape = input.shape
  
      if mode == "opencv" or mode == "_opencv_edge":
          if matrix.ndim == 1:
              matrix = cupy.diag(matrix)
          coordinates = cupy.indices(output_shape, dtype=cupy.float64)
          coordinates = cupy.dot(
              matrix, coordinates.reshape((input.ndim, -1))
          )
          coordinates += cupy.expand_dims(cupy.asarray(offset), -1)
          ret = _util._get_output(output, input, shape=output_shape)
          ret[:] = map_coordinates(
              input,
              coordinates,
              ret.dtype,
              order,
              mode,
              cval,
              prefilter,
          ).reshape(output_shape)
          return ret
  
      matrix = matrix.astype(cupy.float64, copy=False)
      ndim = input.ndim
      output = _util._get_output(output, input, shape=output_shape)
      if input.dtype.kind in "iu":
          input = input.astype(cupy.float32)
      filtered, nprepad = _filter_input(
          input, prefilter, mode, cval, order
      )
  
      integer_output = output.dtype.kind in "iu"
      _util._check_cval(mode, cval, integer_output)
      large_int = max(_prod(input.shape), _prod(output_shape)) > 1 << 31
      if matrix.ndim == 1:
          offset = cupy.asarray(offset, dtype=cupy.float64)
          offset = -offset / matrix
          kern = _interp_kernels._get_zoom_shift_kernel(
              ndim,
              large_int,
              output_shape,
              mode,
              cval=cval,
              order=order,
              integer_output=integer_output,
              nprepad=nprepad,
          )
          kern(filtered, offset, matrix, output)
      else:
          kern = _interp_kernels._get_affine_kernel(
              ndim,
              large_int,
              output_shape,
              mode,
              cval=cval,
              order=order,
              integer_output=integer_output,
              nprepad=nprepad,
          )
          m = cupy.zeros((ndim, ndim + 1), dtype=cupy.float64)
          m[:, :-1] = matrix
          m[:, -1] = cupy.asarray(offset, dtype=cupy.float64)
          kern(filtered, m, output)
      return output
  
imports: import math
  import warnings
  import cupy
  import numpy
  from cupy import _core
  from cupy._core import internal
  from cupy.cuda import runtime
  from cupyx import _texture
  from cupyx.scipy.ndimage import _util
  from cupyx.scipy.ndimage import _interp_kernels
  from cupyx.scipy.ndimage import _spline_prefilter_core
label_desc: Use image node_id 9 for calling a global function with example usage: affine_transform(input, matrix, offset, output_shape, output, order, mode, cval, prefilter) and returns: output, _texture, ret