def test_inverse_non_invertible(): raises(NonSquareMatrixError, lambda: Inverse(A)) raises(NonSquareMatrixError, lambda: Inverse(A*B)) raises(NonSquareMatrixError, lambda: ZeroMatrix(n, m).I) raises(NonInvertibleMatrixError, lambda: ZeroMatrix(n, n).I) raises(NonSquareMatrixError, lambda: OneMatrix(n, m).I) raises(NonInvertibleMatrixError, lambda: OneMatrix(2, 2).I)
def test_block_collapse_type(): bm1 = BlockDiagMatrix(ImmutableMatrix([1]), ImmutableMatrix([2])) bm2 = BlockDiagMatrix(ImmutableMatrix([3]), ImmutableMatrix([4])) assert bm1.T.__class__ == BlockDiagMatrix assert block_collapse(bm1 - bm2).__class__ == BlockDiagMatrix assert block_collapse(Inverse(bm1)).__class__ == BlockDiagMatrix assert block_collapse(Transpose(bm1)).__class__ == BlockDiagMatrix assert bc_transpose(Transpose(bm1)).__class__ == BlockDiagMatrix assert bc_inverse(Inverse(bm1)).__class__ == BlockDiagMatrix
def _eval_derivative_matrix_lines(self, x): from sympy.core.expr import ExprBuilder from sympy.codegen.array_utils import ( CodegenArrayContraction, CodegenArrayTensorProduct, ) from .matmul import MatMul from .inverse import Inverse exp = self.exp if self.base.shape == (1, 1) and not exp.has(x): lr = self.base._eval_derivative_matrix_lines(x) for i in lr: subexpr = ExprBuilder( CodegenArrayContraction, [ ExprBuilder( CodegenArrayTensorProduct, [ Identity(1), i._lines[0], exp * self.base**(exp - 1), i._lines[1], Identity(1), ], ), (0, 3, 4), (5, 7, 8), ], validator=CodegenArrayContraction._validate, ) i._first_pointer_parent = subexpr.args[0].args i._first_pointer_index = 0 i._second_pointer_parent = subexpr.args[0].args i._second_pointer_index = 4 i._lines = [subexpr] return lr if (exp > 0) == True: newexpr = MatMul.fromiter([self.base for i in range(exp)]) elif (exp == -1) == True: return Inverse(self.base)._eval_derivative_matrix_lines(x) elif (exp < 0) == True: newexpr = MatMul.fromiter( [Inverse(self.base) for i in range(-exp)]) elif (exp == 0) == True: return self.doit()._eval_derivative_matrix_lines(x) else: raise NotImplementedError("cannot evaluate %s derived by %s" % (self, x)) return newexpr._eval_derivative_matrix_lines(x)
def _eval_derivative_matrix_lines(self, x): from .matmul import MatMul from .inverse import Inverse exp = self.exp if (exp > 0) == True: newexpr = MatMul.fromiter([self.base for i in range(exp)]) elif (exp == -1) == True: return Inverse(self.base)._eval_derivative_matrix_lines(x) elif (exp < 0) == True: newexpr = MatMul.fromiter([Inverse(self.base) for i in range(-exp)]) elif (exp == 0) == True: return self.doit()._eval_derivative_matrix_lines(x) else: raise NotImplementedError("cannot evaluate %s derived by %s" % (self, x)) return newexpr._eval_derivative_matrix_lines(x)
def doit(self, **kwargs): if kwargs.get('deep', True): base, exp = [arg.doit(**kwargs) for arg in self.args] else: base, exp = self.args # combine all powers, e.g. (A ** 2) ** 3 -> A ** 6 while isinstance(base, MatPow): exp *= base.args[1] base = base.args[0] if isinstance(base, MatrixBase): # Delegate return base**exp # Handle simple cases so that _eval_power() in MatrixExpr sub-classes can ignore them if exp == S.One: return base if exp == S.Zero: return Identity(base.rows) if exp == S.NegativeOne: from sympy.matrices.expressions import Inverse return Inverse(base).doit(**kwargs) eval_power = getattr(base, '_eval_power', None) if eval_power is not None: return eval_power(exp) return MatPow(base, exp)
def doit(self, **kwargs): from sympy.matrices.expressions import Inverse deep = kwargs.get('deep', True) if deep: args = [arg.doit(**kwargs) for arg in self.args] else: args = self.args base, exp = args # combine all powers, e.g. (A**2)**3 = A**6 while isinstance(base, MatPow): exp = exp * base.args[1] base = base.args[0] if exp.is_zero and base.is_square: if isinstance(base, MatrixBase): return base.func(Identity(base.shape[0])) return Identity(base.shape[0]) elif isinstance(base, ZeroMatrix) and exp.is_negative: raise ValueError("Matrix determinant is 0, not invertible.") elif isinstance(base, (Identity, ZeroMatrix)): return base elif isinstance(base, MatrixBase): if exp is S.One: return base return base**exp # Note: just evaluate cases we know, return unevaluated on others. # E.g., MatrixSymbol('x', n, m) to power 0 is not an error. elif exp is S.NegativeOne and base.is_square: return Inverse(base).doit(**kwargs) elif exp is S.One: return base return MatPow(base, exp)
def test_squareBlockMatrix(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) Y = BlockMatrix([[A]]) assert X.is_square assert (block_collapse(X + Identity(m + n)) == BlockMatrix([[A + Identity(n), B], [C, D + Identity(m)]])) Q = X + Identity(m + n) assert block_collapse(Q.inverse()) == Inverse(block_collapse(Q)) assert (X + MatrixSymbol('Q', n + m, n + m)).is_MatAdd assert (X * MatrixSymbol('Q', n + m, n + m)).is_MatMul assert Y.I.blocks[0, 0] == A.I assert X.inverse(expand=True) == BlockMatrix([ [(-B*D.I*C + A).I, -A.I*B*(D + -C*A.I*B).I], [-(D - C*A.I*B).I*C*A.I, (D - C*A.I*B).I]]) assert isinstance(X.inverse(expand=False), Inverse) assert isinstance(X.inverse(), Inverse) assert not X.is_Identity Z = BlockMatrix([[Identity(n), B], [C, D]]) assert not Z.is_Identity
def test_tensorflow_matrices(): if not tf: skip("TensorFlow not installed") expr = M assert tensorflow_code(expr) == "M" _compare_tensorflow_matrix((M, ), expr) expr = M + N assert tensorflow_code(expr) == "tensorflow.math.add(M, N)" _compare_tensorflow_matrix((M, N), expr) expr = M * N assert tensorflow_code(expr) == "tensorflow.linalg.matmul(M, N)" _compare_tensorflow_matrix((M, N), expr) expr = HadamardProduct(M, N) assert tensorflow_code(expr) == "tensorflow.math.multiply(M, N)" _compare_tensorflow_matrix((M, N), expr) expr = M * N * P * Q assert tensorflow_code(expr) == \ "tensorflow.linalg.matmul(" \ "tensorflow.linalg.matmul(" \ "tensorflow.linalg.matmul(M, N), P), Q)" _compare_tensorflow_matrix((M, N, P, Q), expr) expr = M**3 assert tensorflow_code(expr) == \ "tensorflow.linalg.matmul(tensorflow.linalg.matmul(M, M), M)" _compare_tensorflow_matrix((M, ), expr) expr = Trace(M) assert tensorflow_code(expr) == "tensorflow.linalg.trace(M)" _compare_tensorflow_matrix((M, ), expr) expr = Determinant(M) assert tensorflow_code(expr) == "tensorflow.linalg.det(M)" _compare_tensorflow_matrix_scalar((M, ), expr) expr = Inverse(M) assert tensorflow_code(expr) == "tensorflow.linalg.inv(M)" _compare_tensorflow_matrix_inverse((M, ), expr, use_float=True) expr = M.T assert tensorflow_code(expr, tensorflow_version='1.14') == \ "tensorflow.linalg.matrix_transpose(M)" assert tensorflow_code(expr, tensorflow_version='1.13') == \ "tensorflow.matrix_transpose(M)" _compare_tensorflow_matrix((M, ), expr)
def test_inverse(): raises(ShapeError, lambda: Inverse(A)) raises(ShapeError, lambda: Inverse(A * B)) assert Inverse(C).shape == (n, n) assert Inverse(A * E).shape == (n, n) assert Inverse(E * A).shape == (m, m) assert Inverse(C).inverse() == C assert isinstance(Inverse(Inverse(C)), Inverse) assert C.inverse().inverse() == C assert C.inverse() * C == Identity(C.rows) assert Identity(n).inverse() == Identity(n) assert (3 * Identity(n)).inverse() == Identity(n) / 3 # Simplifies Muls if possible (i.e. submatrices are square) assert (C * D).inverse() == D.I * C.I # But still works when not possible assert isinstance((A * E).inverse(), Inverse) assert Inverse(eye(3)).doit() == eye(3) assert Inverse(eye(3)).doit(deep=False) == eye(3)
def test_PermutationMatrix_inverse(): P = PermutationMatrix(Permutation(0, 1, 2)) assert Inverse(P).doit() == PermutationMatrix(Permutation(0, 2, 1))
def test_inverse_matpow_canonicalization(): A = MatrixSymbol('A', 3, 3) assert Inverse(MatPow(A, 3)).doit() == MatPow(Inverse(A), 3).doit()
def test_inverse(): assert Inverse(C).args == (C, S.NegativeOne) assert Inverse(C).shape == (n, n) assert Inverse(A*E).shape == (n, n) assert Inverse(E*A).shape == (m, m) assert Inverse(C).inverse() == C assert isinstance(Inverse(Inverse(C)), Inverse) assert Inverse(*Inverse(E*A).args) == Inverse(E*A) assert C.inverse().inverse() == C assert C.inverse()*C == Identity(C.rows) assert Identity(n).inverse() == Identity(n) assert (3*Identity(n)).inverse() == Identity(n)/3 # Simplifies Muls if possible (i.e. submatrices are square) assert (C*D).inverse() == D.I*C.I # But still works when not possible assert isinstance((A*E).inverse(), Inverse) assert Inverse(C*D).doit(inv_expand=False) == Inverse(C*D) assert Inverse(eye(3)).doit() == eye(3) assert Inverse(eye(3)).doit(deep=False) == eye(3) assert OneMatrix(1, 1).I == Identity(1) assert isinstance(OneMatrix(n, n).I, Inverse)