""" Merge explicit MatrixBase arguments >>> from sympy import MatrixSymbol, eye, Matrix, MatAdd, pprint >>> from sympy.matrices.expressions.matadd import merge_explicit >>> A = MatrixSymbol('A', 2, 2) >>> B = eye(2) >>> C = Matrix([[1, 2], [3, 4]]) >>> X = MatAdd(A, B, C) >>> pprint(X) [1 0] [1 2] A + [ ] + [ ] [0 1] [3 4] >>> pprint(merge_explicit(X)) [2 2] A + [ ] [3 5] """ groups = sift(matadd.args, lambda arg: isinstance(arg, MatrixBase)) if len(groups[True]) > 1: return MatAdd(*(groups[False] + [reduce(add, groups[True])])) else: return matadd rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), merge_explicit, sort(default_sort_key)) canonicalize = exhaust( condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
>>> from sympy.matrices.expressions.matadd import merge_explicit >>> A = MatrixSymbol('A', 2, 2) >>> B = eye(2) >>> C = Matrix([[1, 2], [3, 4]]) >>> X = MatAdd(A, B, C) >>> pprint(X) [1 0] [1 2] A + [ ] + [ ] [0 1] [3 4] >>> pprint(merge_explicit(X)) [2 2] A + [ ] [3 5] """ groups = sift(matadd.args, lambda arg: isinstance(arg, MatrixBase)) if len(groups[True]) > 1: return MatAdd(*(groups[False] + [reduce(add, groups[True])])) else: return matadd rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), merge_explicit, sort(default_sort_key)) canonicalize = exhaust(condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
def canonicalize(x): """Canonicalize the Hadamard product ``x`` with mathematical properties. Examples ======== >>> from sympy.matrices.expressions import MatrixSymbol, HadamardProduct >>> from sympy.matrices.expressions import OneMatrix, ZeroMatrix >>> from sympy.matrices.expressions.hadamard import canonicalize >>> from sympy import init_printing >>> init_printing(use_unicode=False) >>> A = MatrixSymbol('A', 2, 2) >>> B = MatrixSymbol('B', 2, 2) >>> C = MatrixSymbol('C', 2, 2) Hadamard product associativity: >>> X = HadamardProduct(A, HadamardProduct(B, C)) >>> X A.*(B.*C) >>> canonicalize(X) A.*B.*C Hadamard product commutativity: >>> X = HadamardProduct(A, B) >>> Y = HadamardProduct(B, A) >>> X A.*B >>> Y B.*A >>> canonicalize(X) A.*B >>> canonicalize(Y) A.*B Hadamard product identity: >>> X = HadamardProduct(A, OneMatrix(2, 2)) >>> X A.*1 >>> canonicalize(X) A Absorbing element of Hadamard product: >>> X = HadamardProduct(A, ZeroMatrix(2, 2)) >>> X A.*0 >>> canonicalize(X) 0 Rewriting to Hadamard Power >>> X = HadamardProduct(A, A, A) >>> X A.*A.*A >>> canonicalize(X) .3 A Notes ===== As the Hadamard product is associative, nested products can be flattened. The Hadamard product is commutative so that factors can be sorted for canonical form. A matrix of only ones is an identity for Hadamard product, so every matrices of only ones can be removed. Any zero matrix will make the whole product a zero matrix. Duplicate elements can be collected and rewritten as HadamardPower References ========== .. [1] https://en.wikipedia.org/wiki/Hadamard_product_(matrices) """ # Associativity rule = condition( lambda x: isinstance(x, HadamardProduct), flatten ) fun = exhaust(rule) x = fun(x) # Identity fun = condition( lambda x: isinstance(x, HadamardProduct), rm_id(lambda x: isinstance(x, OneMatrix)) ) x = fun(x) # Absorbing by Zero Matrix def absorb(x): if any(isinstance(c, ZeroMatrix) for c in x.args): return ZeroMatrix(*x.shape) else: return x fun = condition( lambda x: isinstance(x, HadamardProduct), absorb ) x = fun(x) # Rewriting with HadamardPower if isinstance(x, HadamardProduct): from collections import Counter tally = Counter(x.args) new_arg = [] for base, exp in tally.items(): if exp == 1: new_arg.append(base) else: new_arg.append(HadamardPower(base, exp)) x = HadamardProduct(*new_arg) # Commutativity fun = condition( lambda x: isinstance(x, HadamardProduct), sort(default_sort_key) ) x = fun(x) # Unpacking x = unpack(x) return x
def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned" % (A, B)) factor_of = lambda arg: arg.as_coeff_mmul()[0] matrix_of = lambda arg: unpack(arg.as_coeff_mmul()[1]) def combine(cnt, mat): from .matmul import MatMul if cnt == 1: return mat else: return cnt * mat rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), sort(default_sort_key)) canonicalize = exhaust( condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
def canonicalize(x): """Canonicalize the Hadamard product ``x`` with mathematical properties. Examples ======== >>> from sympy.matrices.expressions import MatrixSymbol, HadamardProduct >>> from sympy.matrices.expressions import OneMatrix, ZeroMatrix >>> from sympy.matrices.expressions.hadamard import canonicalize >>> A = MatrixSymbol('A', 2, 2) >>> B = MatrixSymbol('B', 2, 2) >>> C = MatrixSymbol('C', 2, 2) Hadamard product associativity: >>> X = HadamardProduct(A, HadamardProduct(B, C)) >>> X A.*(B.*C) >>> canonicalize(X) A.*B.*C Hadamard product commutativity: >>> X = HadamardProduct(A, B) >>> Y = HadamardProduct(B, A) >>> X A.*B >>> Y B.*A >>> canonicalize(X) A.*B >>> canonicalize(Y) A.*B Hadamard product identity: >>> X = HadamardProduct(A, OneMatrix(2, 2)) >>> X A.*OneMatrix(2, 2) >>> canonicalize(X) A Absorbing element of Hadamard product: >>> X = HadamardProduct(A, ZeroMatrix(2, 2)) >>> X A.*0 >>> canonicalize(X) 0 Rewriting to Hadamard Power >>> X = HadamardProduct(A, A, A) >>> X A.*A.*A >>> canonicalize(X) A.**3 Notes ===== As the Hadamard product is associative, nested products can be flattened. The Hadamard product is commutative so that factors can be sorted for canonical form. A matrix of only ones is an identity for Hadamard product, so every matrices of only ones can be removed. Any zero matrix will make the whole product a zero matrix. Duplicate elements can be collected and rewritten as HadamardPower References ========== .. [1] https://en.wikipedia.org/wiki/Hadamard_product_(matrices) """ from sympy.core.compatibility import default_sort_key # Associativity rule = condition( lambda x: isinstance(x, HadamardProduct), flatten ) fun = exhaust(rule) x = fun(x) # Identity fun = condition( lambda x: isinstance(x, HadamardProduct), rm_id(lambda x: isinstance(x, OneMatrix)) ) x = fun(x) # Absorbing by Zero Matrix def absorb(x): if any(isinstance(c, ZeroMatrix) for c in x.args): return ZeroMatrix(*x.shape) else: return x fun = condition( lambda x: isinstance(x, HadamardProduct), absorb ) x = fun(x) # Rewriting with HadamardPower if isinstance(x, HadamardProduct): from collections import Counter tally = Counter(x.args) new_arg = [] for base, exp in tally.items(): if exp == 1: new_arg.append(base) else: new_arg.append(HadamardPower(base, exp)) x = HadamardProduct(*new_arg) # Commutativity fun = condition( lambda x: isinstance(x, HadamardProduct), sort(default_sort_key) ) x = fun(x) # Unpacking x = unpack(x) return x
>>> from sympy.matrices.expressions.matadd import merge_explicit >>> A = MatrixSymbol('A', 2, 2) >>> B = eye(2) >>> C = Matrix([[1, 2], [3, 4]]) >>> X = MatAdd(A, B, C) >>> pprint(X) [1 0] [1 2] A + [ ] + [ ] [0 1] [3 4] >>> pprint(merge_explicit(X)) [2 2] A + [ ] [3 5] """ groups = sift(matadd.args, lambda arg: isinstance(arg, MatrixBase)) if len(groups[True]) > 1: return MatAdd(*(groups[False] + [reduce(add, groups[True])])) else: return matadd rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), merge_explicit, sort(default_sort_key)) canonicalize = exhaust(condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
return MatAdd(*[Trace(arg) for arg in self.args]).doit() def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned"%(A, B)) factor_of = lambda arg: arg.as_coeff_mmul()[0] matrix_of = lambda arg: unpack(arg.as_coeff_mmul()[1]) def combine(cnt, mat): from matmul import MatMul if cnt == 1: return mat else: return cnt * mat rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), sort(str)) canonicalize = exhaust(condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
# there has to be some bug deep inside the core, I absolutely need this # function to correctly perform addition def recreate_args(args): return [a.func(*a.args) for a in args] # Adapted from sympy.matrices.expressions.matadd.py groups = sift(vecadd.args, lambda arg: isinstance(arg, (Vector))) if len(groups[True]) > 1: return VecAdd(*(recreate_args(groups[False]) + [reduce(add, recreate_args(groups[True]))])) # return VecAdd(*(groups[False] + [reduce(add, groups[True])])) else: return vecadd rules = (merge_explicit, sort(default_sort_key)) canonicalize = exhaust( condition(lambda x: isinstance(x, VecAdd), do_one(*rules))) class VecMul(VectorExpr, Mul): """ A product of Vector expressions. VecMul inherits from and operates like SymPy Mul. """ is_VecMul = True is_commutative = True def __new__(cls, *args, **kwargs): evaluate = kwargs.get('evaluate', True)
def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned" % (A, B)) factor_of = lambda arg: arg.as_coeff_mmul()[0] matrix_of = lambda arg: unpack(arg.as_coeff_mmul()[1]) def combine(cnt, mat): from matmul import MatMul if cnt == 1: return mat else: return cnt * mat rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), sort(str)) canonicalize = exhaust( condition(lambda x: isinstance(x, MatAdd), do_one(*rules)))
return super(HadamardProduct, cls).__new__(cls, *args) @property def shape(self): return self.args[0].shape def _entry(self, i, j): return Mul(*[arg._entry(i, j) for arg in self.args]) def _eval_transpose(self): from transpose import Transpose return HadamardProduct(*[Transpose(arg) for arg in self.args]) def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned" % (A, B)) rules = (unpack, flatten, sort(str)) canonicalize = exhaust(condition(lambda x: isinstance(x, HadamardProduct), do_one(*rules)))