def doit(self, expand=False): try: if self.args[0].shape[0] == 1: return (self.args[0] * transpose(self.args[1])).doit()[0] else: return (transpose(self.args[0]) * self.args[1]).doit()[0] except (AttributeError, NotImplementedError): return self
def doit(self, expand=False): if self.args[0].shape == self.args[1].shape: if self.args[0].shape[0] == 1: mul = self.args[0]*transpose(self.args[1]) else: mul = transpose(self.args[0])*self.args[1] else: if self.args[0].shape[0] == 1: mul = self.args[0]*self.args[1] else: mul = transpose(self.args[0])*transpose(self.args[1]) return mul[0]
def remove_matelement(expr, i1, i2): def repl_match(pos): def func(x): if not isinstance(x, MatrixElement): return False if x.args[pos] != i1: return False if x.args[3-pos] == 0: if x.args[0].shape[2-pos] == 1: return True else: return False return True return func expr = expr.replace(repl_match(1), lambda x: x.args[0]) expr = expr.replace(repl_match(2), lambda x: transpose(x.args[0])) # Make sure that all Mul are transformed to MatMul and that they # are flattened: rule = bottom_up(lambda x: reduce(lambda a, b: a*b, x.args) if isinstance(x, (Mul, MatMul)) else x) return rule(expr)
def _eval_transpose(self): # Flip all the individual matrices matrices = [transpose(matrix) for matrix in self.blocks] # Make a copy M = Matrix(self.blockshape[0], self.blockshape[1], matrices) # Transpose the block structure M = M.transpose() return BlockMatrix(M)
def _eval_transpose(self): """Transposition of matrix multiplication. Notes ===== The following rules are applied. Transposition for matrix multiplied with another matrix: `\\left(A B\\right)^{T} = B^{T} A^{T}` Transposition for matrix multiplied with scalar: `\\left(c A\\right)^{T} = c A^{T}` References ========== .. [1] https://en.wikipedia.org/wiki/Transpose """ coeff, matrices = self.as_coeff_matrices() return MatMul( coeff, *[transpose(arg) for arg in matrices[::-1]]).doit()
def _eval_conjugate(self): return transpose(self.arg)
def recurse_expr(expr, index_ranges={}): if expr.is_Mul: nonmatargs = [] pos_arg = [] pos_ind = [] dlinks = {} link_ind = [] counter = 0 args_ind = [] for arg in expr.args: retvals = recurse_expr(arg, index_ranges) assert isinstance(retvals, list) if isinstance(retvals, list): for i in retvals: args_ind.append(i) else: args_ind.append(retvals) for arg_symbol, arg_indices in args_ind: if arg_indices is None: nonmatargs.append(arg_symbol) continue if isinstance(arg_symbol, MatrixElement): arg_symbol = arg_symbol.args[0] pos_arg.append(arg_symbol) pos_ind.append(arg_indices) link_ind.append([None]*len(arg_indices)) for i, ind in enumerate(arg_indices): if ind in dlinks: other_i = dlinks[ind] link_ind[counter][i] = other_i link_ind[other_i[0]][other_i[1]] = (counter, i) dlinks[ind] = (counter, i) counter += 1 counter2 = 0 lines = {} while counter2 < len(link_ind): for i, e in enumerate(link_ind): if None in e: line_start_index = (i, e.index(None)) break cur_ind_pos = line_start_index cur_line = [] index1 = pos_ind[cur_ind_pos[0]][cur_ind_pos[1]] while True: d, r = cur_ind_pos if pos_arg[d] != 1: if r % 2 == 1: cur_line.append(transpose(pos_arg[d])) else: cur_line.append(pos_arg[d]) next_ind_pos = link_ind[d][1-r] counter2 += 1 # Mark as visited, there will be no `None` anymore: link_ind[d] = (-1, -1) if next_ind_pos is None: index2 = pos_ind[d][1-r] lines[(index1, index2)] = cur_line break cur_ind_pos = next_ind_pos ret_indices = list(j for i in lines for j in i) lines = {k: MatMul.fromiter(v) if len(v) != 1 else v[0] for k, v in lines.items()} return [(Mul.fromiter(nonmatargs), None)] + [ (MatrixElement(a, i, j), (i, j)) for (i, j), a in lines.items() ] elif expr.is_Add: res = [recurse_expr(i) for i in expr.args] d = collections.defaultdict(list) for res_addend in res: scalar = 1 for elem, indices in res_addend: if indices is None: scalar = elem continue indices = tuple(sorted(indices, key=default_sort_key)) d[indices].append(scalar*remove_matelement(elem, *indices)) scalar = 1 return [(MatrixElement(Add.fromiter(v), *k), k) for k, v in d.items()] elif isinstance(expr, KroneckerDelta): i1, i2 = expr.args if dimensions is not None: identity = Identity(dimensions[0]) else: identity = S.One return [(MatrixElement(identity, i1, i2), (i1, i2))] elif isinstance(expr, MatrixElement): matrix_symbol, i1, i2 = expr.args if i1 in index_ranges: r1, r2 = index_ranges[i1] if r1 != 0 or matrix_symbol.shape[0] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[0])) if i2 in index_ranges: r1, r2 = index_ranges[i2] if r1 != 0 or matrix_symbol.shape[1] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[1])) if (i1 == i2) and (i1 in index_ranges): return [(trace(matrix_symbol), None)] return [(MatrixElement(matrix_symbol, i1, i2), (i1, i2))] elif isinstance(expr, Sum): return recurse_expr( expr.args[0], index_ranges={i[0]: i[1:] for i in expr.args[1:]} ) else: return [(expr, None)]
def transpose(self): from sympy.matrices.expressions.transpose import transpose return transpose(self)
def _eval_transpose(self): return MatAdd(*[transpose(arg) for arg in self.args]).doit()
def _eval_transpose(self): from sympy.matrices.expressions.transpose import transpose return HadamardPower(transpose(self.base), self.exp)
def from_index_summation(expr, first_index=None): r""" Parse expression of matrices with explicitly summed indices into a matrix expression without indices, if possible. This transformation expressed in mathematical notation: `\sum_{j=0}^{N-1} A_{i,j} B_{j,k} \Longrightarrow \mathbf{A}\cdot \mathbf{B}` Optional parameter ``first_index``: specify which free index to use as the index starting the expression. Examples ======== >>> from sympy import MatrixSymbol, MatrixExpr, Sum, Symbol >>> from sympy.abc import i, j, k, l, N >>> A = MatrixSymbol("A", N, N) >>> B = MatrixSymbol("B", N, N) >>> expr = Sum(A[i, j]*B[j, k], (j, 0, N-1)) >>> MatrixExpr.from_index_summation(expr) A*B Transposition is detected: >>> expr = Sum(A[j, i]*B[j, k], (j, 0, N-1)) >>> MatrixExpr.from_index_summation(expr) A.T*B Detect the trace: >>> expr = Sum(A[i, i], (i, 0, N-1)) >>> MatrixExpr.from_index_summation(expr) Trace(A) More complicated expressions: >>> expr = Sum(A[i, j]*B[k, j]*A[l, k], (j, 0, N-1), (k, 0, N-1)) >>> MatrixExpr.from_index_summation(expr) A*B.T*A.T """ from sympy import Sum, Mul, MatMul, transpose, trace def recurse_expr(expr, index_ranges={}): if expr.is_Mul: nonmatargs = [] matargs = [] pos_arg = [] pos_ind = [] dlinks = {} link_ind = [] counter = 0 for arg in expr.args: arg_symbol, arg_indices = recurse_expr(arg, index_ranges) if arg_indices is None: nonmatargs.append(arg_symbol) continue i1, i2 = arg_indices pos_arg.append(arg_symbol) pos_ind.append(i1) pos_ind.append(i2) link_ind.extend([None, None]) if i1 in dlinks: other_i1 = dlinks[i1] link_ind[2*counter] = other_i1 link_ind[other_i1] = 2*counter if i2 in dlinks: other_i2 = dlinks[i2] link_ind[2*counter + 1] = other_i2 link_ind[other_i2] = 2*counter + 1 dlinks[i1] = 2*counter dlinks[i2] = 2*counter + 1 counter += 1 cur_ind_pos = link_ind.index(None) first_index = pos_ind[cur_ind_pos] while True: d = cur_ind_pos // 2 r = cur_ind_pos % 2 if r == 1: matargs.append(transpose(pos_arg[d])) else: matargs.append(pos_arg[d]) next_ind_pos = link_ind[2*d + 1 - r] if next_ind_pos is None: last_index = pos_ind[2*d + 1 - r] break cur_ind_pos = next_ind_pos return Mul.fromiter(nonmatargs)*MatMul.fromiter(matargs), (first_index, last_index) elif expr.is_Add: res = [recurse_expr(i) for i in expr.args] res = [ ((transpose(i), (j[1], j[0])) if default_sort_key(j[0]) > default_sort_key(j[1]) else (i, j)) for (i, j) in res ] addends, last_indices = zip(*res) last_indices = list(set(last_indices)) if len(last_indices) > 1: print(last_indices) raise ValueError("incompatible summation") return MatAdd.fromiter(addends), last_indices[0] elif isinstance(expr, KroneckerDelta): i1, i2 = expr.args return S.One, (i1, i2) elif isinstance(expr, MatrixElement): matrix_symbol, i1, i2 = expr.args if i1 in index_ranges: r1, r2 = index_ranges[i1] if r1 != 0 or matrix_symbol.shape[0] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[0])) if i2 in index_ranges: r1, r2 = index_ranges[i2] if r1 != 0 or matrix_symbol.shape[1] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[1])) if (i1 == i2) and (i1 in index_ranges): return trace(matrix_symbol), None return matrix_symbol, (i1, i2) elif isinstance(expr, Sum): return recurse_expr( expr.args[0], index_ranges={i[0]: i[1:] for i in expr.args[1:]} ) else: return expr, None parsed, irange = recurse_expr(expr) if irange is None or first_index is None: return parsed i1, i2 = irange if i1 == first_index: return parsed if i2 == first_index: return transpose(parsed) raise ValueError("no such index")
def _eval_transpose(self): return MatMul(*[transpose(arg) for arg in self.args[::-1]]).doit()
def recurse_expr(expr, index_ranges={}): if expr.is_Mul: nonmatargs = [] matargs = [] pos_arg = [] pos_ind = [] dlinks = {} link_ind = [] counter = 0 for arg in expr.args: arg_symbol, arg_indices = recurse_expr(arg, index_ranges) if arg_indices is None: nonmatargs.append(arg_symbol) continue i1, i2 = arg_indices pos_arg.append(arg_symbol) pos_ind.append(i1) pos_ind.append(i2) link_ind.extend([None, None]) if i1 in dlinks: other_i1 = dlinks[i1] link_ind[2*counter] = other_i1 link_ind[other_i1] = 2*counter if i2 in dlinks: other_i2 = dlinks[i2] link_ind[2*counter + 1] = other_i2 link_ind[other_i2] = 2*counter + 1 dlinks[i1] = 2*counter dlinks[i2] = 2*counter + 1 counter += 1 cur_ind_pos = link_ind.index(None) first_index = pos_ind[cur_ind_pos] while True: d = cur_ind_pos // 2 r = cur_ind_pos % 2 if r == 1: matargs.append(transpose(pos_arg[d])) else: matargs.append(pos_arg[d]) next_ind_pos = link_ind[2*d + 1 - r] if next_ind_pos is None: last_index = pos_ind[2*d + 1 - r] break cur_ind_pos = next_ind_pos return Mul.fromiter(nonmatargs)*MatMul.fromiter(matargs), (first_index, last_index) elif expr.is_Add: res = [recurse_expr(i) for i in expr.args] res = [ ((transpose(i), (j[1], j[0])) if default_sort_key(j[0]) > default_sort_key(j[1]) else (i, j)) for (i, j) in res ] addends, last_indices = zip(*res) last_indices = list(set(last_indices)) if len(last_indices) > 1: print(last_indices) raise ValueError("incompatible summation") return MatAdd.fromiter(addends), last_indices[0] elif isinstance(expr, KroneckerDelta): i1, i2 = expr.args return S.One, (i1, i2) elif isinstance(expr, MatrixElement): matrix_symbol, i1, i2 = expr.args if i1 in index_ranges: r1, r2 = index_ranges[i1] if r1 != 0 or matrix_symbol.shape[0] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[0])) if i2 in index_ranges: r1, r2 = index_ranges[i2] if r1 != 0 or matrix_symbol.shape[1] != r2+1: raise ValueError("index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[1])) if (i1 == i2) and (i1 in index_ranges): return trace(matrix_symbol), None return matrix_symbol, (i1, i2) elif isinstance(expr, Sum): return recurse_expr( expr.args[0], index_ranges={i[0]: i[1:] for i in expr.args[1:]} ) else: return expr, None