def block_collapse(expr): """Evaluates a block matrix expression >>> 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]]) >>> B Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> C Matrix([[I, Z]]) >>> block_collapse(C*B) Matrix([[X, Z + Z*Y]]) """ def hasbm(expr): return 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) return result.doit()
def __new__(cls, *computations): computations = tuple(unique(computations)) computations = exhaust(flatten)(computations) computations = exhaust(rm_identity)(computations) if len(computations) == 1: return computations[0] else: obj = object.__new__(cls) obj.computations = tuple(computations) return obj
def block_collapse(expr): """Evaluates a block matrix expression >>> 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]]) >>> B Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> C Matrix([[I, Z]]) >>> block_collapse(C*B) Matrix([[X, Z + Z*Y]]) """ def hasbm(expr): return 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) return result.doit()
if result != mmul: return newmul(factor, *result.args) # Recombine and return else: return mul def factor_in_front(mul): factor, matrices = mul.as_coeff_matrices() if factor != 1: return newmul(factor, *matrices) return mul rules = (any_zeros, remove_ids, xxinv, unpack, rm_id(lambda x: x == 1), merge_explicit, factor_in_front, flatten) canonicalize = exhaust(typed({MatMul: do_one(rules)})) def only_squares(*matrices): """ factor matrices only if they are square """ if matrices[0].rows != matrices[-1].cols: raise RuntimeError("Invalid matrices being multiplied") out = [] start = 0 for i, M in enumerate(matrices): if M.cols == matrices[start].rows: out.append(MatMul(*matrices[start:i + 1]).doit()) start = i + 1 return out
>>> A = MatrixSymbol('A', 2, 2) >>> B = eye(2) >>> C = Matrix([[1, 2], [3, 4]]) >>> X = MatAdd(A, B, C) >>> pprint(X, use_unicode=False) [1 0] [1 2] A + [ ] + [ ] [0 1] [3 4] >>> pprint(merge_explicit(X), use_unicode=False) [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)))
>>> A = MatrixSymbol('A', 2, 2) >>> B = eye(2) >>> C = Matrix([[1, 2], [3, 4]]) >>> X = MatAdd(A, B, C) >>> pprint(X, use_unicode=False) A + [1 0] + [1 2] [ ] [ ] [0 1] [3 4] >>> pprint(merge_explicit(X), use_unicode=False) A + [2 2] [ ] [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)))
if result != mmul: return newmul(factor, *result.args) # Recombine and return else: return mul def factor_in_front(mul): factor, matrices = mul.as_coeff_matrices() if factor != 1: return newmul(factor, *matrices) return mul rules = (any_zeros, remove_ids, xxinv, unpack, rm_id(lambda x: x == 1), merge_explicit, factor_in_front, flatten) canonicalize = exhaust(typed({MatMul: do_one(rules)})) def only_squares(*matrices): """factor matrices only if they are square.""" if matrices[0].rows != matrices[-1].cols: raise RuntimeError("Invalid matrices being multiplied") out = [] start = 0 for i, M in enumerate(matrices): if M.cols == matrices[start].rows: out.append(MatMul(*matrices[start:i+1]).doit()) start = i+1 return out
return super().__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(*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)))
return super().__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(*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)))