code: def register(reg_name):
      """Register a subclass of CustomOpProp to the registry with name reg_name."""
  
      def do_register(prop_cls):
          """Register a subclass of CustomOpProp to the registry."""
          fb_functype = CFUNCTYPE(
              c_int,
              c_int,
              POINTER(c_void_p),
              POINTER(c_int),
              POINTER(c_int),
              c_int,
              c_void_p,
          )
          del_functype = CFUNCTYPE(c_int, c_void_p)
  
          infershape_functype = CFUNCTYPE(
              c_int,
              c_int,
              POINTER(c_int),
              POINTER(POINTER(mx_int)),
              c_void_p,
          )
          infertype_functype = CFUNCTYPE(
              c_int, c_int, POINTER(c_int), c_void_p
          )
          inferstorage_functype = CFUNCTYPE(
              c_int, c_int, POINTER(c_int), c_void_p
          )
          inferstorage_backward_functype = CFUNCTYPE(
              c_int, c_int, POINTER(c_int), POINTER(c_int), c_void_p
          )
          list_functype = CFUNCTYPE(
              c_int, POINTER(POINTER(POINTER(c_char))), c_void_p
          )
          deps_functype = CFUNCTYPE(
              c_int,
              c_int_p,
              c_int_p,
              c_int_p,
              c_int_p,
              POINTER(c_int_p),
              c_void_p,
          )
          createop_functype = CFUNCTYPE(
              c_int,
              c_char_p,
              c_int,
              POINTER(POINTER(mx_uint)),
              POINTER(c_int),
              POINTER(c_int),
              POINTER(MXCallbackList),
              c_void_p,
          )
          req_enum = ("null", "write", "inplace", "add")
          create_ndarray_fn = (
              _np_ndarray_cls if is_np_array() else _ndarray_cls
          )
  
          def creator(op_type, argc, keys, vals, ret):
              """internal function"""
              assert py_str(op_type) == reg_name
              kwargs = {}
              for i in range(argc):
                  key = py_str(keys[i])
                  if key not in [
                      "__ctx_group__",
                      "__lr_mult__",
                      "__wd_mult__",
                      "__force_mirroring__",
                      "__mirror_stage__",
                      "__profiler_scope__",
                  ]:
                      kwargs[key] = py_str(vals[i])
              op_prop = prop_cls(**kwargs)
  
              def infer_shape_entry(
                  num_tensor, tensor_dims, tensor_shapes, _
              ):
                  """C Callback for ``CustomOpProp::InferShape``."""
                  try:
                      n_in = len(op_prop.list_arguments())
                      n_out = len(op_prop.list_outputs())
                      n_aux = len(op_prop.list_auxiliary_states())
                      assert num_tensor == n_in + n_out + n_aux
  
                      shapes = [
                          [
                              tensor_shapes[i][j]
                              for j in range(tensor_dims[i])
                          ]
                          for i in range(n_in)
                      ]
                      ret = op_prop.infer_shape(shapes)
                      if len(ret) == 2:
                          ishape, oshape = ret
                          ashape = []
                      elif len(ret) == 3:
                          ishape, oshape, ashape = ret
                      else:
                          raise AssertionError(
                              "infer_shape must return 2 or 3 lists"
                          )
                      assert len(oshape) == n_out, (
                          f"InferShape Error: expecting {n_out} entries in returned output "
                          f"shapes, got {len(oshape)}."
                      )
                      assert len(ishape) == n_in, (
                          f"InferShape Error: expecting {n_in} entries in returned input "
                          f"shapes, got {len(ishape)}."
                      )
                      assert len(ashape) == n_aux, (
                          f"InferShape Error: expecting {n_aux} entries in returned aux state "
                          f"shapes, got {len(ashape)}."
                      )
                      rshape = (
                          list(ishape) + list(oshape) + list(ashape)
                      )
                      for i in range(n_in + n_out + n_aux):
                          tensor_shapes[i] = cast(
                              c_array_buf(
                                  mx_int, array("i", rshape[i])
                              ),
                              POINTER(mx_int),
                          )
                          tensor_dims[i] = len(rshape[i])
  
                      infer_shape_entry._ref_holder = [tensor_shapes]
                  except Exception:
                      print(
                          f"Error in {reg_name}.infer_shape: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def infer_storage_type_backward_entry(
                  num_tensor, tensor_stypes, tags, _
              ):
                  # pylint: disable=C0301
                  """C Callback for CustomOpProp::InferStorageTypeBackward"""
                  try:
                      tensors = [[] for i in range(5)]
                      for i in range(num_tensor):
                          tensors[tags[i]].append(
                              _STORAGE_TYPE_ID_TO_STR[tensor_stypes[i]]
                          )
                      # Ordering of stypes: ograd, input, output, igrad, aux
                      tensors = [
                          tensors[3],
                          tensors[0],
                          tensors[1],
                          tensors[2],
                          tensors[4],
                      ]
                      ret = op_prop.infer_storage_type_backward(
                          tensors[0],
                          tensors[1],
                          tensors[2],
                          tensors[3],
                          tensors[4],
                      )
                      if len(ret) == 4:
                          ret += []
                      elif len(ret) == 5:
                          pass
                      else:
                          raise AssertionError(
                              "infer_storage_type_backward must return 4 or 5 lists"
                          )
                      assert len(ret[0]) == len(tensors[0]), (
                          f"InferStorageTypeBackward Error: expecting == {len(tensors[0])} "
                          "entries in returned output gradient "
                          f"stypes, got {len(ret[0])}."
                      )
                      assert len(ret[1]) == len(tensors[1]), (
                          f"InferStorageTypeBackward Error: expecting == {len(tensors[1])} "
                          "entries in returned input stypes, "
                          f"got {len(ret[1])}."
                      )
                      assert len(ret[2]) == len(tensors[2]), (
                          f"InferStorageTypeBackward Error: expecting == {len(tensors[2])} "
                          "entries in returned output stypes, "
                          f"got {len(ret[2])}."
                      )
                      assert len(ret[3]) == len(tensors[3]), (
                          f"InferStorageTypeBackward Error: expecting == {len(tensors[3])} "
                          "entries in returned input gradient stypes, "
                          f"got {len(ret[3])}."
                      )
                      assert len(ret[4]) == len(tensors[4]), (
                          f"InferStorageTypeBackward Error: expecting == {len(tensors[4])} "
                          "entries in returned aux stypes, "
                          f"got {len(ret[4])}."
                      )
                      rstype = []
                      for ret_list in ret:
                          rstype.extend(ret_list)
  
                      for i, stype in enumerate(rstype):
                          assert (
                              stype
                              != _STORAGE_TYPE_ID_TO_STR[
                                  _STORAGE_TYPE_UNDEFINED
                              ]
                          ), "stype should not be undefined"
                          assert stype in _STORAGE_TYPE_STR_TO_ID, (
                              f"Provided stype: {stype} is not valid "
                              "valid stypes are {}, {}, {}".format(
                                  _STORAGE_TYPE_ID_TO_STR[
                                      _STORAGE_TYPE_DEFAULT
                                  ],
                                  _STORAGE_TYPE_ID_TO_STR[
                                      _STORAGE_TYPE_ROW_SPARSE
                                  ],
                                  _STORAGE_TYPE_ID_TO_STR[
                                      _STORAGE_TYPE_CSR
                                  ],
                              )
                          )
                          tensor_stypes[i] = _STORAGE_TYPE_STR_TO_ID[
                              stype
                          ]
  
                      infer_storage_type_backward_entry._ref_holder = [
                          tensor_stypes
                      ]
                  except Exception:
                      print(
                          f"Error in {reg_name}.infer_type: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def infer_storage_type_entry(
                  num_tensor, tensor_stypes, _
              ):
                  """C Callback for CustomOpProp::InferStorageType"""
                  try:
                      n_in = len(op_prop.list_arguments())
                      n_out = len(op_prop.list_outputs())
                      n_aux = len(op_prop.list_auxiliary_states())
                      assert num_tensor == n_in + n_out + n_aux
  
                      stypes = [
                          _STORAGE_TYPE_ID_TO_STR[tensor_stypes[i]]
                          for i in range(n_in)
                      ]
                      ret = op_prop.infer_storage_type(stypes)
                      if len(ret) == 2:
                          istype, ostype = ret
                          astype = []
                      elif len(ret) == 3:
                          istype, ostype, astype = ret
                      else:
                          raise AssertionError(
                              "infer_storage_type must return 2 or 3 lists"
                          )
  
                      assert len(ostype) == n_out, (
                          f"InferStorageType Error: expecting {n_out} entries in returned output "
                          f"stypes, got {len(ostype)}."
                      )
                      assert len(istype) == n_in, (
                          f"InferStorageType Error: expecting {n_in} entries in returned input "
                          f"stypes, got {len(istype)}."
                      )
                      assert len(astype) == n_aux, (
                          f"InferStorageType Error: expecting {n_aux} entries in returned aux state "
                          f"stypes, got {len(astype)}."
                      )
                      rtype = list(istype) + list(ostype) + list(astype)
                      for i, dtype in enumerate(rtype):
                          tensor_stypes[i] = _STORAGE_TYPE_STR_TO_ID[
                              dtype
                          ]
                      infer_storage_type_entry._ref_holder = [
                          tensor_stypes
                      ]
                  except Exception:
                      print(
                          f"Error in {reg_name}.infer_type: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def infer_type_entry(num_tensor, tensor_types, _):
                  """C Callback for CustomOpProp::InferType"""
                  try:
                      n_in = len(op_prop.list_arguments())
                      n_out = len(op_prop.list_outputs())
                      n_aux = len(op_prop.list_auxiliary_states())
                      assert num_tensor == n_in + n_out + n_aux
  
                      types = [
                          dtype_mx_to_np(tensor_types[i])
                          for i in range(n_in)
                      ]
                      ret = op_prop.infer_type(types)
                      if len(ret) == 2:
                          itype, otype = ret
                          atype = []
                      elif len(ret) == 3:
                          itype, otype, atype = ret
                      else:
                          raise AssertionError(
                              "infer_type must return 2 or 3 lists"
                          )
                      assert len(otype) == n_out, (
                          f"InferType Error: expecting {n_out} entries in returned output "
                          f"types, got {len(otype)}."
                      )
                      assert len(itype) == n_in, (
                          f"InferType Error: expecting {n_in} entries in returned input "
                          f"types, got {len(itype)}."
                      )
                      assert len(atype) == n_aux, (
                          f"InferType Error: expecting {n_aux} entries in returned aux state "
                          f"types, got {len(atype)}."
                      )
                      rtype = list(itype) + list(otype) + list(atype)
                      for i, dtype in enumerate(rtype):
                          tensor_types[i] = dtype_np_to_mx(dtype)
  
                      infer_type_entry._ref_holder = [tensor_types]
                  except Exception:
                      print(
                          f"Error in {reg_name}.infer_type: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def list_outputs_entry(out, _):
                  """C Callback for CustomOpProp::ListOutputs"""
                  try:
                      ret = op_prop.list_outputs()
                      ret = [c_str(i) for i in ret] + [c_char_p(0)]
                      ret = c_array(c_char_p, ret)
                      out[0] = cast(ret, POINTER(POINTER(c_char)))
  
                      list_outputs_entry._ref_holder = [out]
                  except Exception:
                      print(
                          f"Error in {reg_name}.list_outputs: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def list_arguments_entry(out, _):
                  """C Callback for CustomOpProp::ListArguments"""
                  try:
                      ret = op_prop.list_arguments()
                      ret = [c_str(i) for i in ret] + [c_char_p(0)]
                      ret = c_array(c_char_p, ret)
                      out[0] = cast(ret, POINTER(POINTER(c_char)))
  
                      list_arguments_entry._ref_holder = [out]
                  except Exception:
                      print(
                          f"Error in {reg_name}.list_arguments: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              def list_auxiliary_states_entry(out, _):
                  """C Callback for CustomOpProp::ListAuxiliaryStates"""
                  try:
                      ret = op_prop.list_auxiliary_states()
                      ret = [c_str(i) for i in ret] + [c_char_p(0)]
                      ret = c_array(c_char_p, ret)
                      out[0] = cast(ret, POINTER(POINTER(c_char)))
  
                      list_auxiliary_states_entry._ref_holder = [out]
                  except Exception:
                      tb = traceback.format_exc()
                      print(
                          f"Error in {reg_name}.list_auxiliary_states: {tb}"
                      )
                      return False
                  return True
  
              def declare_backward_dependency_entry(
                  out_grad, in_data, out_data, num_dep, deps, _
              ):
                  """C Callback for CustomOpProp::DeclareBacwardDependency"""
                  try:
                      out_grad = [
                          out_grad[i]
                          for i in range(len(op_prop.list_outputs()))
                      ]
                      in_data = [
                          in_data[i]
                          for i in range(len(op_prop.list_arguments()))
                      ]
                      out_data = [
                          out_data[i]
                          for i in range(len(op_prop.list_outputs()))
                      ]
                      rdeps = op_prop.declare_backward_dependency(
                          out_grad, in_data, out_data
                      )
                      num_dep[0] = len(rdeps)
                      _registry.result_deps = set()
                      for dep in rdeps:
                          _registry.result_deps.add(dep)
                      rdeps = cast(
                          c_array_buf(c_int, array("i", rdeps)), c_int_p
                      )
                      deps[0] = rdeps
  
                      declare_backward_dependency_entry._ref_holder = [
                          deps
                      ]
                  except Exception:
                      tb = traceback.format_exc()
                      print(
                          f"Error in {reg_name}.declare_backward_dependency: {tb}"
                      )
                      return False
                  return True
  
              def create_operator_entry(
                  ctx, num_inputs, shapes, ndims, dtypes, ret, _
              ):
                  """C Callback for CustomOpProp::CreateOperator"""
                  try:
                      ctx = py_str(ctx)
                      sep = ctx.find("(")
                      ctx = context.Context(
                          ctx[:sep], int(ctx[sep + 1 : -1])
                      )
                      ndims = [ndims[i] for i in range(num_inputs)]
                      shapes = [
                          [shapes[i][j] for j in range(ndims[i])]
                          for i in range(num_inputs)
                      ]
                      dtypes = [dtypes[i] for i in range(num_inputs)]
                      op = op_prop.create_operator(ctx, shapes, dtypes)
  
                      def forward_entry(
                          num_ndarray,
                          ndarraies,
                          tags,
                          reqs,
                          is_train,
                          _,
                      ):
                          """C Callback for CustomOp::Forward"""
                          try:
                              tensors = [[] for i in range(5)]
                              for i in range(num_ndarray):
                                  if tags[i] == 1 or tags[i] == 4:
                                      tensors[tags[i]].append(
                                          create_ndarray_fn(
                                              cast(
                                                  ndarraies[i],
                                                  NDArrayHandle,
                                              ),
                                              writable=True,
                                          )
                                      )
                                  else:
                                      tensors[tags[i]].append(
                                          create_ndarray_fn(
                                              cast(
                                                  ndarraies[i],
                                                  NDArrayHandle,
                                              ),
                                              writable=False,
                                          )
                                      )
                              reqs = [
                                  req_enum[reqs[i]]
                                  for i in range(len(tensors[1]))
                              ]
                              with ctx:
                                  op.forward(
                                      is_train=is_train,
                                      req=reqs,
                                      in_data=tensors[0],
                                      out_data=tensors[1],
                                      aux=tensors[4],
                                  )
                          except Exception:
                              print(
                                  f"Error in CustomOp.forward: {traceback.format_exc()}"
                              )
                              return False
                          return True
  
                      def backward_entry(
                          num_ndarray,
                          ndarraies,
                          tags,
                          reqs,
                          is_train,
                          _,
                      ):
                          """C Callback for CustomOp::Backward"""
                          # pylint: disable=W0613
                          try:
                              tensors = [[] for i in range(5)]
                              num_outputs = len(op_prop.list_outputs())
                              num_args = len(op_prop.list_arguments())
                              for i in range(num_ndarray):
                                  if (
                                      i in _registry.result_deps
                                      or i
                                      >= (num_outputs * 2 + num_args)
                                  ):
                                      # If it is a backward dependency or output or aux:
                                      # Set stype as undefined so that it returns
                                      # ndarray based on existing stype
                                      stype = _STORAGE_TYPE_UNDEFINED
                                  else:
                                      # If it is some input, output or out grad ndarray not part of
                                      # backward dependency it is empty and thus the ndarray should
                                      # be set to default
                                      stype = _STORAGE_TYPE_DEFAULT
                                  if tags[i] == 2 or tags[i] == 4:
                                      tensors[tags[i]].append(
                                          create_ndarray_fn(
                                              cast(
                                                  ndarraies[i],
                                                  NDArrayHandle,
                                              ),
                                              writable=True,
                                              stype=stype,
                                          )
                                      )
                                  else:
                                      tensors[tags[i]].append(
                                          create_ndarray_fn(
                                              cast(
                                                  ndarraies[i],
                                                  NDArrayHandle,
                                              ),
                                              writable=False,
                                              stype=stype,
                                          )
                                      )
                              reqs = [
                                  req_enum[reqs[i]]
                                  for i in range(len(tensors[2]))
                              ]
                              with ctx:
                                  op.backward(
                                      req=reqs,
                                      in_data=tensors[0],
                                      out_data=tensors[1],
                                      in_grad=tensors[2],
                                      out_grad=tensors[3],
                                      aux=tensors[4],
                                  )
                          except Exception:
                              print(
                                  f"Error in CustomOp.backward: {traceback.format_exc()}"
                              )
                              return False
                          return True
  
                      cur = _registry.inc()
  
                      def delete_entry(_):
                          """C Callback for CustomOp::del"""
                          try:
                              del _registry.ref_holder[cur]
                          except Exception:
                              print(
                                  f"Error in CustomOp.delete: {traceback.format_exc()}"
                              )
                              return False
                          return True
  
                      callbacks = [
                          del_functype(delete_entry),
                          fb_functype(forward_entry),
                          fb_functype(backward_entry),
                      ]
                      callbacks = [
                          cast(i, CFUNCTYPE(c_int)) for i in callbacks
                      ]
                      contexts = [None, None, None]
                      ret[0] = MXCallbackList(
                          c_int(len(callbacks)),
                          cast(
                              c_array(CFUNCTYPE(c_int), callbacks),
                              POINTER(CFUNCTYPE(c_int)),
                          ),
                          cast(
                              c_array(c_void_p, contexts),
                              POINTER(c_void_p),
                          ),
                      )
                      op._ref_holder = [ret]
                      _registry.ref_holder[cur] = op
                  except Exception:
                      print(
                          f"Error in {reg_name}.create_operator: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              cur = _registry.inc()
  
              def delete_entry(_):
                  """C Callback for CustomOpProp::del"""
                  try:
                      del _registry.ref_holder[cur]
                  except Exception:
                      print(
                          f"Error in CustomOpProp.delete: {traceback.format_exc()}"
                      )
                      return False
                  return True
  
              callbacks = [
                  del_functype(delete_entry),
                  list_functype(list_arguments_entry),
                  list_functype(list_outputs_entry),
                  list_functype(list_auxiliary_states_entry),
                  infershape_functype(infer_shape_entry),
                  deps_functype(declare_backward_dependency_entry),
                  createop_functype(create_operator_entry),
                  infertype_functype(infer_type_entry),
                  inferstorage_functype(infer_storage_type_entry),
                  inferstorage_backward_functype(
                      infer_storage_type_backward_entry
                  ),
              ]
              callbacks = [cast(i, CFUNCTYPE(c_int)) for i in callbacks]
              contexts = [None] * len(callbacks)
              ret[0] = MXCallbackList(
                  c_int(len(callbacks)),
                  cast(
                      c_array(CFUNCTYPE(c_int), callbacks),
                      POINTER(CFUNCTYPE(c_int)),
                  ),
                  cast(c_array(c_void_p, contexts), POINTER(c_void_p)),
              )
              op_prop._ref_holder = [ret]
              _registry.ref_holder[cur] = op_prop
              return True
  
          creator_functype = CFUNCTYPE(
              c_int,
              c_char_p,
              c_int,
              POINTER(c_char_p),
              POINTER(c_char_p),
              POINTER(MXCallbackList),
          )
          creator_func = creator_functype(creator)
          check_call(
              _LIB.MXCustomOpRegister(c_str(reg_name), creator_func)
          )
          cur = _registry.inc()
          _registry.ref_holder[cur] = creator_func
          return prop_cls
  
      return do_register
  
imports: import traceback
  import warnings
  import collections
  from array import array
  from threading import Lock
  import ctypes
  from ctypes import CFUNCTYPE, POINTER, Structure, pointer
  from ctypes import c_void_p, c_int, c_char, c_char_p, cast, c_bool
  from .base import _LIB, check_call, MXCallbackList, c_array, c_array_buf, mx_int, OpHandle
  from .base import c_str, mx_uint, mx_float, ctypes2numpy_shared, NDArrayHandle, py_str
  from .None import symbol, context
  from .ndarray import NDArray, dtype_np_to_mx, dtype_mx_to_np
  from .ndarray.ndarray import _STORAGE_TYPE_STR_TO_ID, _STORAGE_TYPE_ID_TO_STR
  from .ndarray.ndarray import _STORAGE_TYPE_UNDEFINED, _STORAGE_TYPE_DEFAULT
  from .ndarray.ndarray import _STORAGE_TYPE_CSR, _STORAGE_TYPE_ROW_SPARSE
  from .ndarray import _ndarray_cls
  from .numpy.multiarray import _np_ndarray_cls
  from .util import is_np_array
label_desc: Use image node_id 1 for calling a global function with example usage: register(reg_name) and returns: do_register, prop_cls, True, True, True, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False