<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def bind(self, ctx, args, args_grad=None, grad_req='write', aux_states=None, group2ctx=None, shared_exec=None):
 """Binds the current symbol to an executor and returns it. We first declare the computation and then bind to the data to run. This function returns an executor which provides method `forward()` method for evaluation and a `outputs()` method to get all the results. Example ------- <Symbol _plus1> [<NDArray 2x3 @cpu(0)>] [[ 2. 2. 2.] [ 2. 2. 2.]] Parameters ctx : Context The device context the generated executor to run on. args : list of NDArray or dict of str to NDArray Input arguments to the symbol. - If the input type is a list of `NDArray`, the order should be same as the order of `list_arguments()`. - If the input type is a dict of str to `NDArray`, then it maps the name of arguments to the corresponding `NDArray`. - In either case, all the arguments must be provided. args_grad : list of NDArray or dict of str to `NDArray`, optional When specified, `args_grad` provides NDArrays to hold the result of gradient value in backward. - If the input type is a list of `NDArray`, the order should be same as the order of `list_arguments()`. - If the input type is a dict of str to `NDArray`, then it maps the name of arguments to the corresponding NDArray. - When the type is a dict of str to `NDArray`, one only need to provide the dict for required argument gradient. Only the specified argument gradient will be calculated. grad_req : {'write', 'add', 'null'}, or list of str or dict of str to str, optional To specify how we should update the gradient to the `args_grad`. - 'write' means everytime gradient is write to specified `args_grad` `NDArray`. - 'add' means everytime gradient is add to the specified NDArray. - 'null' means no action is taken, the gradient may not be calculated. aux_states : list of `NDArray`, or dict of str to `NDArray`, optional Input auxiliary states to the symbol, only needed when the output of `list_auxiliary_states()` is not empty. - If the input type is a list of `NDArray`, the order should be same as the order of `list_auxiliary_states()`. - If the input type is a dict of str to `NDArray`, then it maps the name of `auxiliary_states` to the corresponding `NDArray`, - In either case, all the auxiliary states need to be provided. group2ctx : Dict of string to mx.Context The dict mapping the `ctx_group` attribute to the context assignment. shared_exec : mx.executor.Executor Executor to share memory with. This is intended for runtime reshaping, variable length sequences, etc. The returned executor shares state with `shared_exec`, and should not be used in parallel with it. Returns ------- executor : Executor The generated executor Notes ----- Auxiliary states are the special states of symbols that do not correspond to an argument, and do not have gradient but are still useful for the specific operations. Common examples of auxiliary states include the `moving_mean` and `moving_variance` states in `BatchNorm`. Most operators do not have auxiliary states and in those cases, this parameter can be safely ignored. One can give up gradient by using a dict in `args_grad` and only specify gradient they interested in. """

        # pylint: disable=too-many-locals, too-many-branches
        if not isinstance(ctx, Context):
            raise TypeError("Context type error")

        listed_arguments = self.list_arguments()
        args_handle, args = self._get_ndarray_inputs('args', args, listed_arguments, False)
        # setup args gradient
        if args_grad is None:
            args_grad_handle = c_array(NDArrayHandle, [None] * len(args))
        else:
            args_grad_handle, args_grad = self._get_ndarray_inputs(
                'args_grad', args_grad, listed_arguments, True)

        if aux_states is None:
            aux_states = []
        aux_args_handle, aux_states = self._get_ndarray_inputs(
            'aux_states', aux_states, self.list_auxiliary_states(), False)

        # setup requirements
        if isinstance(grad_req, string_types):
            if grad_req not in _GRAD_REQ_MAP:
                raise ValueError('grad_req must be in %s' % str(_GRAD_REQ_MAP))
            reqs_array = c_array_buf(mx_uint,
                                     array('I', [_GRAD_REQ_MAP[grad_req]] * len(listed_arguments)))
        elif isinstance(grad_req, list):
            reqs_array = c_array_buf(mx_uint,
                                     array('I', [_GRAD_REQ_MAP[item] for item in grad_req]))
        elif isinstance(grad_req, dict):
            req_array = []
            for name in listed_arguments:
                if name in grad_req:
                    req_array.append(_GRAD_REQ_MAP[grad_req[name]])
                else:
                    req_array.append(0)
            reqs_array = c_array_buf(mx_uint, array('I', req_array))

        ctx_map_keys = []
        ctx_map_dev_types = []
        ctx_map_dev_ids = []

        if group2ctx:
            for key, val in group2ctx.items():
                ctx_map_keys.append(key)
                ctx_map_dev_types.append(val.device_typeid)
                ctx_map_dev_ids.append(val.device_id)

        handle = ExecutorHandle()
        shared_handle = shared_exec.handle if shared_exec is not None else ExecutorHandle()
        check_call(_LIB.MXExecutorBindEX(self.handle,
                                         ctypes.c_int(ctx.device_typeid),
                                         ctypes.c_int(ctx.device_id),
                                         mx_uint(len(ctx_map_keys)),
                                         c_str_array(ctx_map_keys),
                                         c_array_buf(ctypes.c_int, array('i', ctx_map_dev_types)),
                                         c_array_buf(ctypes.c_int, array('i', ctx_map_dev_ids)),
                                         mx_uint(len(args)),
                                         args_handle,
                                         args_grad_handle,
                                         reqs_array,
                                         mx_uint(len(aux_states)),
                                         aux_args_handle,
                                         shared_handle,
                                         ctypes.byref(handle)))
        executor = Executor(handle, self, ctx, grad_req, group2ctx)
        executor.arg_arrays = args
        executor.grad_arrays = args_grad
        executor.aux_arrays = aux_states
        return executor