def METHOD_NAME(ctx, root_grad_output, inverse_grad_output):
    # Taken from http://homepages.inf.ed.ac.uk/imurray2/pub/16choldiff/choldiff.pdf
    if any(ctx.needs_input_grad):
        def is_empty(tensor):
            return tensor.numel() == 0 or (tensor.numel() == 1 and tensor[0] == 0)
        # Fix outputs and gradients
        if is_empty(root_grad_output):
            root_grad_output = None
        if is_empty(inverse_grad_output):
            inverse_grad_output = None
        # Get saved tensors
        matrix_args = ctx.saved_tensors[:-3]
        q_mat = ctx.saved_tensors[-3]
        root_evals = ctx.saved_tensors[-2]
        inverse = ctx.saved_tensors[-1]
        is_batch = False
        if root_grad_output is not None:
            if root_grad_output.ndimension() == 2 and q_mat.ndimension() > 2:
                root_grad_output = root_grad_output.unsqueeze(0)
                is_batch = True
            if root_grad_output.ndimension() == 3 and q_mat.ndimension() > 3:
                root_grad_output = root_grad_output.unsqueeze(0)
                is_batch = True
        if inverse_grad_output is not None:
            if inverse_grad_output.ndimension() == 2 and q_mat.ndimension() > 2:
                inverse_grad_output = inverse_grad_output.unsqueeze(0)
                is_batch = True
            if inverse_grad_output.ndimension() == 3 and q_mat.ndimension() > 3:
                inverse_grad_output = inverse_grad_output.unsqueeze(0)
                is_batch = True
        # Get closure for matmul
        if hasattr(ctx, "_linear_op"):
            linear_op = ctx._linear_op
        else:
            linear_op = ctx.representation_tree(*matrix_args)
        # Get root inverse
        if not ctx.inverse:
            inverse = q_mat / root_evals.unsqueeze(-2)
        # Left factor:
        left_factor = torch.zeros_like(inverse)
        if root_grad_output is not None:
            left_factor.add_(root_grad_output)
        if inverse_grad_output is not None:
            # -root^-T grad_output.T root^-T
            left_factor.sub_(torch.matmul(inverse, inverse_grad_output.mT).matmul(inverse))
        # Right factor
        right_factor = inverse.div(2.0)
        # Fix batches
        if is_batch:
            left_factor = left_factor.permute(1, 0, 2, 3).contiguous()
            left_factor = left_factor.view(inverse.size(1), -1, left_factor.size(-1))
            right_factor = right_factor.permute(1, 0, 2, 3).contiguous()
            right_factor = right_factor.view(inverse.size(1), -1, right_factor.size(-1))
        else:
            left_factor = left_factor.contiguous()
            right_factor = right_factor.contiguous()
        res = linear_op._bilinear_derivative(left_factor, right_factor)
        return tuple([None] * 9 + list(res))
    else:
        pass