'Gives the (sub/super)gradient of the expression w.r.t. each variable.
Matrix expressions are vectorized, so the gradient is a matrix.
None indicates variable values unknown or outside domain.
Returns:
A map of variable to SciPy CSC sparse matrix or None.'

@property
def grad(self):

    if self.is_constant():
        return u.grad.constant_grad(self)
    old_vals = {var.id: var.value for var in self.variables()}
    fix_vars = []
    for var in self.variables():
        if (var.value is None):
            return u.grad.error_grad(self)
        else:
            fix_vars += [(var == var.value)]
    prob = Problem(self._prob.objective, (fix_vars + self._prob.constraints))
    prob.solve()
    if (prob.status in s.SOLUTION_PRESENT):
        sign = (self.is_convex() - self.is_concave())
        lagr = self._prob.objective.args[0]
        for constr in self._prob.constraints:
            lagr_multiplier = self.cast_to_const((sign * constr.dual_value))
            lagr += trace((lagr_multiplier.T * constr._expr))
        grad_map = lagr.grad
        result = {var: grad_map[var] for var in self.variables()}
    else:
        result = u.grad.error_grad(self)
    for var in self.variables():
        var.value = old_vals[var.id]
    return result
