'Gives the (sub/super)gradient of the atom w.r.t. each argument.
Matrix expressions are vectorized, so the gradient is a matrix.
Takes axis into account.
Args:
values: A list of numeric values for the arguments.
Returns:
A list of SciPy CSC sparse matrices or None.'

def _axis_grad(self, values):

    m = self.args[0].size[0]
    n = self.args[0].size[1]
    if (self.axis is None):
        value = np.reshape(values[0].T, ((m * n), 1))
        D = self._column_grad(value)
        if (D is not None):
            D = sp.csc_matrix(D)
    elif (self.axis == 0):
        D = sp.csc_matrix(((m * n), n), dtype=np.float)
        for i in range(n):
            value = values[0][:, i]
            d = self._column_grad(value).T
            if (d is None):
                return [None]
            row = np.linspace((i * n), (((i * n) + m) - 1), m)
            col = (np.ones(m) * i)
            D = (D + sp.csc_matrix((np.array(d)[0], (row, col)), shape=((m * n), n)))
    else:
        values = np.transpose(values[0])
        D = sp.csc_matrix(((m * n), m), dtype=np.float)
        for i in range(m):
            value = values[:, i]
            d = self._column_grad(value).T
            if (d is None):
                return [None]
            row = np.linspace(i, (i + ((n - 1) * m)), n)
            col = (np.ones(n) * i)
            D = (D + sp.csc_matrix((np.array(d)[0], (row, col)), shape=((m * n), m)))
    return [D]
