code: def gspmm_real(gidx, op, reduce_op, X, Y):
      out, (argX, argY) = _gspmm(gidx, op, reduce_op, X, Y)
  
      def grad(dZ):
          dZ = tensor(dZ)
          if op != "copy_rhs":
              g_rev = gidx.reverse()
              if reduce_op == "sum":
                  if op in ["mul", "div"]:
                      dX = _gspmm(
                          g_rev, "mul", "sum", dZ, _muldiv(op, Y)
                      )[0]
                  elif op in ["add", "sub"]:
                      dX = _gspmm(g_rev, "copy_lhs", "sum", dZ, Y)[0]
                  elif op == "copy_lhs":
                      dX = _gspmm(g_rev, "copy_lhs", "sum", dZ, None)[0]
              else:
                  if op in ["mul", "div"]:
                      dX = _scatter_nd(
                          argX,
                          _muldiv(
                              op,
                              _gather_nd(
                                  argY, _expand(Y, dZ.shape[1:])
                              ),
                          )
                          * dZ,
                          X.shape[0],
                      )
                  elif op in ["add", "sub", "copy_lhs"]:
                      dX = _scatter_nd(argX, dZ, X.shape[0])
              dX = _reduce_grad(dX, X.shape)
          else:
              dX = tf.zeros_like(X)
          if op != "copy_lhs":
              if reduce_op == "sum":
                  if op == "mul" and _need_reduce_last_dim(X, Y):
                      dY = _gsddmm(gidx, "dot", X, dZ)
                  elif op in ["mul", "div"]:
                      dY = _gsddmm(gidx, "mul", X, dZ)
                      if op == "div":
                          dY = -dY / (Y**2)
                  elif op in ["add", "sub", "copy_rhs"]:
                      dY = _gsddmm(gidx, "copy_rhs", X, _addsub(op, dZ))
              else:
                  out_shp = (Y.shape[0],) + dZ.shape[1:]
                  if op in ["mul", "div"]:
                      dY = _scatter_nd(
                          argY,
                          _gather_nd(argX, _expand(X, dZ.shape[1:]))
                          * dZ,
                          Y.shape[0],
                      )
                      if op == "div":
                          dY = -dY / (Y**2)
                  elif op in ["add", "sub", "copy_rhs"]:
                      dY = _scatter_nd(
                          argY, _addsub(op, dZ), Y.shape[0]
                      )
              dY = _reduce_grad(dY, Y.shape)
          else:
              dY = tf.zeros_like(Y)
          return dX, dY
  
      return out, grad
  
imports: import numpy
  import tensorflow
  from ..._sparse_ops import _bwd_segment_cmp, _csrmask, _csrmm, _csrsum, _gsddmm, _gspmm, _scatter_add, _segment_reduce
  from ...base import ALL, is_all
  from ...heterograph_index import create_unitgraph_from_csr
  from .tensor import asnumpy, context, copy_to, tensor, zerocopy_from_numpy
label_desc: Use image node_id 8 for calling a global function with example usage: gspmm_real(gidx, op, reduce_op, X, Y) and returns: out,  grad, dX,  dY