def combine_kronecker(expr): """Combine KronekeckerProduct with expression. If possible write operations on KroneckerProducts of compatible shapes as a single KroneckerProduct. Examples ======== >>> from sympy.matrices.expressions import MatrixSymbol, KroneckerProduct, combine_kronecker >>> from sympy import symbols >>> m, n = symbols(r'm, n', integer=True) >>> A = MatrixSymbol('A', m, n) >>> B = MatrixSymbol('B', n, m) >>> combine_kronecker(KroneckerProduct(A, B)*KroneckerProduct(B, A)) KroneckerProduct(A*B, B*A) >>> combine_kronecker(KroneckerProduct(A, B)+KroneckerProduct(B.T, A.T)) KroneckerProduct(A + B.T, B + A.T) >>> combine_kronecker(KroneckerProduct(A, B)**m) KroneckerProduct(A**m, B**m) """ def haskron(expr): return isinstance(expr, MatrixExpr) and expr.has(KroneckerProduct) rule = exhaust( bottom_up(exhaust(condition(haskron, typed( {MatAdd: kronecker_mat_add, MatMul: kronecker_mat_mul, MatPow: kronecker_mat_pow}))))) result = rule(expr) try: return result.doit() except AttributeError: return result
def block_collapse(expr): """Evaluates a block matrix expression >>> from sympy import MatrixSymbol, BlockMatrix, symbols, \ Identity, Matrix, ZeroMatrix, block_collapse >>> n,m,l = symbols('n m l') >>> X = MatrixSymbol('X', n, n) >>> Y = MatrixSymbol('Y', m ,m) >>> Z = MatrixSymbol('Z', n, m) >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m, n), Y]]) >>> print(B) Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> print(C) Matrix([[I, Z]]) >>> print(block_collapse(C*B)) Matrix([[X, Z*Y + Z]]) """ hasbm = lambda expr: isinstance(expr, MatrixExpr) and expr.has(BlockMatrix) rule = exhaust( bottom_up(exhaust(condition(hasbm, typed( {MatAdd: do_one(bc_matadd, bc_block_plus_ident), MatMul: do_one(bc_matmul, bc_dist), Transpose: bc_transpose, Inverse: bc_inverse, BlockMatrix: do_one(bc_unpack, deblock)}))))) result = rule(expr) try: return result.doit() except AttributeError: return result
def block_collapse(expr): """Evaluates a block matrix expression >>> from sympy import MatrixSymbol, BlockMatrix, symbols, \ Identity, Matrix, ZeroMatrix, block_collapse >>> n,m,l = symbols('n m l') >>> X = MatrixSymbol('X', n, n) >>> Y = MatrixSymbol('Y', m ,m) >>> Z = MatrixSymbol('Z', n, m) >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m, n), Y]]) >>> print(B) Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> print(C) Matrix([[I, Z]]) >>> print(block_collapse(C*B)) Matrix([[X, Z*Y + Z]]) """ hasbm = lambda expr: isinstance(expr, MatrixExpr) and expr.has(BlockMatrix) rule = exhaust( bottom_up( exhaust( condition( hasbm, typed({ MatAdd: do_one(bc_matadd, bc_block_plus_ident), MatMul: do_one(bc_matmul, bc_dist), Transpose: bc_transpose, Inverse: bc_inverse, BlockMatrix: do_one(bc_unpack, deblock) }))))) result = rule(expr) try: return result.doit() except AttributeError: return result
def combine_kronecker(expr): """Combine KronekeckerProduct with expression. If possible write operations on KroneckerProducts of compatible shapes as a single KroneckerProduct. Examples ======== >>> from sympy.matrices.expressions import MatrixSymbol, KroneckerProduct, combine_kronecker >>> from sympy import symbols >>> m, n = symbols(r'm, n', integer=True) >>> A = MatrixSymbol('A', m, n) >>> B = MatrixSymbol('B', n, m) >>> combine_kronecker(KroneckerProduct(A, B)*KroneckerProduct(B, A)) KroneckerProduct(A*B, B*A) >>> combine_kronecker(KroneckerProduct(A, B)+KroneckerProduct(B.T, A.T)) KroneckerProduct(A + B.T, B + A.T) >>> combine_kronecker(KroneckerProduct(A, B)**m) KroneckerProduct(A**m, B**m) """ def haskron(expr): return isinstance(expr, MatrixExpr) and expr.has(KroneckerProduct) rule = exhaust( bottom_up( exhaust( condition( haskron, typed({ MatAdd: kronecker_mat_add, MatMul: kronecker_mat_mul, MatPow: kronecker_mat_pow }))))) result = rule(expr) doit = getattr(result, 'doit', None) if doit is not None: return doit() else: return result
""" 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)))
def explicit_kronecker_product(kron): # Make sure we have a sequence of Matrices if not all(isinstance(m, MatrixBase) for m in kron.args): return kron return matrix_kronecker_product(*kron.args) rules = (unpack, explicit_kronecker_product, flatten, extract_commutative) canonicalize = exhaust(condition(lambda x: isinstance(x, KroneckerProduct), do_one(*rules))) def _kronecker_dims_key(expr): if isinstance(expr, KroneckerProduct): return tuple(a.shape for a in expr.args) else: return (0,) def kronecker_mat_add(expr): from functools import reduce args = sift(expr.args, _kronecker_dims_key) nonkrons = args.pop((0,), None) if not args: return expr
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) canonicalize = exhaust( condition(lambda x: isinstance(x, HadamardProduct), do_one(*rules))) def hadamard_power(base, exp): base = sympify(base) exp = sympify(exp) if exp == 1: return base if not base.is_Matrix: return base**exp if exp.is_Matrix: raise ValueError("cannot raise expression to a matrix") return HadamardPower(base, exp) class HadamardPower(MatrixExpr):
validate(*args) 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 sympy.matrices.expressions.transpose import transpose return HadamardProduct(*list(map(transpose, 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) canonicalize = exhaust(condition(lambda x: isinstance(x, HadamardProduct), 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
>>> 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 >>> 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
else: return MatrixClass(matrix_expansion) def explicit_kronecker_product(kron): # Make sure we have a sequence of Matrices if not all(isinstance(m, MatrixBase) for m in kron.args): return kron return matrix_kronecker_product(*kron.args) rules = (unpack, explicit_kronecker_product, flatten, extract_commutative) canonicalize = exhaust( condition(lambda x: isinstance(x, KroneckerProduct), do_one(*rules))) def _kronecker_dims_key(expr): if isinstance(expr, KroneckerProduct): return tuple(a.shape for a in expr.args) else: return (0, ) def kronecker_mat_add(expr): from functools import reduce args = sift(expr.args, _kronecker_dims_key) nonkrons = args.pop((0, ), None) if not args: return expr