def __new__ (cls, arg, **options): # print "Inverse(", arg, ")" # print INVERTIBLE if isinstance(arg, Inverse): return arg.args[0] if arg.is_Number: return 1 / arg arg_rank = expr_rank(arg) if arg_rank == 1: raise NotInvertibleError if is_one(arg): return arg if is_zero(arg): raise NotInvertibleError # FIXME: Funky case trying to catch lower triangular or diagonal # muls like T(P_0)*A*P_0 if arg in INVERTIBLE: pass elif isinstance(arg, TensorExpr) and not arg.has_inverse: raise NotInvertibleError elif isinstance(arg, Mul): if arg.args[0] == S(-1): return - Inverse(reduce(operator.mul, arg.args[1:])) if not expr_invertible(arg): raise NotInvertibleError options['commutative'] = arg.is_commutative return Basic.__new__(cls, arg, **options)
def expr_invertible(expr): if expr.is_Number: return True if isinstance(expr, TensorExpr) and expr.has_inverse: return True elif isinstance(expr, Pow): return expr_invertible(expr.args[0]) if isinstance(expr, Inverse): return True if expr in INVERTIBLE: return True er = expr_rank(expr) if er == 1: return False if is_one(expr): return True if is_zero(expr): return False # FIXME: Funky case trying to catch lower triangular or diagonal # muls like T(P_0)*A*P_0 if isinstance(expr, Mul): return reduce(lambda acc, x: acc and expr_invertible(x), expr.args, True) if isinstance(expr, Pow): return expr_invertible(expr.args[0])