def test_matrix_derivative_with_inverse(): # Cookbook example 61: expr = a.T * Inverse(X) * b assert expr.diff(X) == -Inverse(X).T * a * b.T * Inverse(X).T # Cookbook example 63: expr = Trace(A * Inverse(X) * B) assert expr.diff(X) == -(X**(-1) * B * A * X**(-1)).T # Cookbook example 64: expr = Trace(Inverse(X + A)) assert expr.diff(X) == -(Inverse(X + A) * Inverse(X + A)).T
def affine_shape_parametrization_from_vertices_mapping(dim, vertices_mapping): # Check if the "identity" string is provided, and return this trivial case if isinstance(vertices_mapping, str): assert vertices_mapping.lower() == "identity" return tuple(["x[" + str(i) + "]" for i in range(dim)]) # Get a sympy symbol for mu mu = strings_to_sympy_symbolic_parameters( itertools.chain(*vertices_mapping.values()), MatrixSymbol) # Convert vertices from string to symbols vertices_mapping_symbolic = dict() for (reference_vertex, deformed_vertex) in vertices_mapping.items(): assert isinstance(reference_vertex, Matrix) == isinstance(deformed_vertex, Matrix) if isinstance(reference_vertex, Matrix): reference_vertex_symbolic = reference_vertex deformed_vertex_symbolic = deformed_vertex else: reference_vertex_symbolic = python_string_to_sympy( reference_vertex, None, None) deformed_vertex_symbolic = python_string_to_sympy( deformed_vertex, None, mu) assert reference_vertex_symbolic not in vertices_mapping_symbolic vertices_mapping_symbolic[ reference_vertex_symbolic] = deformed_vertex_symbolic # Find A and b such that x_o = A x + b for all (x, x_o) in vertices_mapping lhs = zeros(dim + dim**2, dim + dim**2) rhs = zeros(dim + dim**2, 1) for (offset, (reference_vertex, deformed_vertex)) in enumerate(vertices_mapping_symbolic.items()): for i in range(dim): rhs[offset * dim + i] = deformed_vertex[i] lhs[offset * dim + i, i] = 1 for j in range(dim): lhs[offset * dim + i, (i + 1) * dim + j] = reference_vertex[j] solution = Inverse(lhs) * rhs b = zeros(dim, 1) for i in range(dim): b[i] = solution[i] A = zeros(dim, dim) for i in range(dim): for j in range(dim): A[i, j] = solution[dim + i * dim + j] # Convert into an expression x = sympy_symbolic_coordinates(dim, MatrixListSymbol) x_o = A * x + b return tuple([str(x_o[i]).replace(", 0]", "]") for i in range(dim)])
def test_matrix_derivative_with_inverse(): # Cookbook example 61: expr = a.T * Inverse(X) * b assert expr.diff(X) == -Inverse(X).T * a * b.T * Inverse(X).T # Cookbook example 62: expr = Determinant(Inverse(X)) # Not implemented yet: # assert expr.diff(X) == -Determinant(X.inv())*(X.inv()).T # Cookbook example 63: expr = Trace(A * Inverse(X) * B) assert expr.diff(X) == -(X**(-1) * B * A * X**(-1)).T # Cookbook example 64: expr = Trace(Inverse(X + A)) assert expr.diff(X) == -(Inverse(X + A)).T**2
def test_parsing_of_matrix_expressions(): expr = M * N assert parse_matrix_expression(expr) == CodegenArrayContraction( CodegenArrayTensorProduct(M, N), (1, 2)) expr = Transpose(M) assert parse_matrix_expression(expr) == CodegenArrayPermuteDims(M, [1, 0]) expr = M * Transpose(N) assert parse_matrix_expression(expr) == CodegenArrayContraction( CodegenArrayTensorProduct(M, CodegenArrayPermuteDims(N, [1, 0])), (1, 2)) expr = 3 * M * N res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr == rexpr expr = 3 * M + N * M.T * M + 4 * k * N res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr == rexpr expr = Inverse(M) * N rexpr = recognize_matrix_expression(parse_matrix_expression(expr)) assert expr == rexpr expr = M**2 rexpr = recognize_matrix_expression(parse_matrix_expression(expr)) assert expr == rexpr expr = M * (2 * N + 3 * M) res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr.expand() == rexpr.doit() expr = Trace(M) result = CodegenArrayContraction(M, (0, 1)) assert parse_matrix_expression(expr) == result
def test_matrix_derivatives_of_traces(): expr = Trace(A) * A assert expr.diff(A) == Derivative(Trace(A) * A, A) assert expr[i, j].diff( A[m, n]).doit() == (KDelta(i, m) * KDelta(j, n) * Trace(A) + KDelta(m, n) * A[i, j]) ## First order: # Cookbook example 99: expr = Trace(X) assert expr.diff(X) == Identity(k) assert expr.rewrite(Sum).diff(X[m, n]).doit() == KDelta(m, n) # Cookbook example 100: expr = Trace(X * A) assert expr.diff(X) == A.T assert expr.rewrite(Sum).diff(X[m, n]).doit() == A[n, m] # Cookbook example 101: expr = Trace(A * X * B) assert expr.diff(X) == A.T * B.T assert expr.rewrite(Sum).diff(X[m, n]).doit().dummy_eq((A.T * B.T)[m, n]) # Cookbook example 102: expr = Trace(A * X.T * B) assert expr.diff(X) == B * A # Cookbook example 103: expr = Trace(X.T * A) assert expr.diff(X) == A # Cookbook example 104: expr = Trace(A * X.T) assert expr.diff(X) == A # Cookbook example 105: # TODO: TensorProduct is not supported #expr = Trace(TensorProduct(A, X)) #assert expr.diff(X) == Trace(A)*Identity(k) ## Second order: # Cookbook example 106: expr = Trace(X**2) assert expr.diff(X) == 2 * X.T # Cookbook example 107: expr = Trace(X**2 * B) assert expr.diff(X) == (X * B + B * X).T expr = Trace(MatMul(X, X, B)) assert expr.diff(X) == (X * B + B * X).T # Cookbook example 108: expr = Trace(X.T * B * X) assert expr.diff(X) == B * X + B.T * X # Cookbook example 109: expr = Trace(B * X * X.T) assert expr.diff(X) == B * X + B.T * X # Cookbook example 110: expr = Trace(X * X.T * B) assert expr.diff(X) == B * X + B.T * X # Cookbook example 111: expr = Trace(X * B * X.T) assert expr.diff(X) == X * B.T + X * B # Cookbook example 112: expr = Trace(B * X.T * X) assert expr.diff(X) == X * B.T + X * B # Cookbook example 113: expr = Trace(X.T * X * B) assert expr.diff(X) == X * B.T + X * B # Cookbook example 114: expr = Trace(A * X * B * X) assert expr.diff(X) == A.T * X.T * B.T + B.T * X.T * A.T # Cookbook example 115: expr = Trace(X.T * X) assert expr.diff(X) == 2 * X expr = Trace(X * X.T) assert expr.diff(X) == 2 * X # Cookbook example 116: expr = Trace(B.T * X.T * C * X * B) assert expr.diff(X) == C.T * X * B * B.T + C * X * B * B.T # Cookbook example 117: expr = Trace(X.T * B * X * C) assert expr.diff(X) == B * X * C + B.T * X * C.T # Cookbook example 118: expr = Trace(A * X * B * X.T * C) assert expr.diff(X) == A.T * C.T * X * B.T + C * A * X * B # Cookbook example 119: expr = Trace((A * X * B + C) * (A * X * B + C).T) assert expr.diff(X) == 2 * A.T * (A * X * B + C) * B.T # Cookbook example 120: # TODO: no support for TensorProduct. # expr = Trace(TensorProduct(X, X)) # expr = Trace(X)*Trace(X) # expr.diff(X) == 2*Trace(X)*Identity(k) # Higher Order # Cookbook example 121: expr = Trace(X**k) #assert expr.diff(X) == k*(X**(k-1)).T # Cookbook example 122: expr = Trace(A * X**k) #assert expr.diff(X) == # Needs indices # Cookbook example 123: expr = Trace(B.T * X.T * C * X * X.T * C * X * B) assert expr.diff( X ) == C * X * X.T * C * X * B * B.T + C.T * X * B * B.T * X.T * C.T * X + C * X * B * B.T * X.T * C * X + C.T * X * X.T * C.T * X * B * B.T # Other # Cookbook example 124: expr = Trace(A * X**(-1) * B) assert expr.diff(X) == -Inverse(X).T * A.T * B.T * Inverse(X).T # Cookbook example 125: expr = Trace(Inverse(X.T * C * X) * A) # Warning: result in the cookbook is equivalent if B and C are symmetric: assert expr.diff(X) == -X.inv().T * A.T * X.inv() * C.inv().T * X.inv( ).T - X.inv().T * A * X.inv() * C.inv() * X.inv().T # Cookbook example 126: expr = Trace((X.T * C * X).inv() * (X.T * B * X)) assert expr.diff(X) == -2 * C * X * (X.T * C * X).inv() * X.T * B * X * ( X.T * C * X).inv() + 2 * B * X * (X.T * C * X).inv() # Cookbook example 127: expr = Trace((A + X.T * C * X).inv() * (X.T * B * X)) # Warning: result in the cookbook is equivalent if B and C are symmetric: assert expr.diff(X) == B * X * Inverse(A + X.T * C * X) - C * X * Inverse( A + X.T * C * X) * X.T * B * X * Inverse(A + X.T * C * X) - C.T * X * Inverse( A.T + (C * X).T * X) * X.T * B.T * X * Inverse( A.T + (C * X).T * X) + B.T * X * Inverse(A.T + (C * X).T * X)
def test_parsing_of_matrix_expressions(): expr = M*N assert parse_matrix_expression(expr) == CodegenArrayContraction(CodegenArrayTensorProduct(M, N), (1, 2)) expr = Transpose(M) assert parse_matrix_expression(expr) == CodegenArrayPermuteDims(M, [1, 0]) expr = M*Transpose(N) assert parse_matrix_expression(expr) == CodegenArrayContraction(CodegenArrayTensorProduct(M, CodegenArrayPermuteDims(N, [1, 0])), (1, 2)) expr = 3*M*N res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr == rexpr expr = 3*M + N*M.T*M + 4*k*N res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr == rexpr expr = Inverse(M)*N rexpr = recognize_matrix_expression(parse_matrix_expression(expr)) assert expr == rexpr expr = M**2 rexpr = recognize_matrix_expression(parse_matrix_expression(expr)) assert expr == rexpr expr = M*(2*N + 3*M) res = parse_matrix_expression(expr) rexpr = recognize_matrix_expression(res) assert expr == rexpr expr = Trace(M) result = CodegenArrayContraction(M, (0, 1)) assert parse_matrix_expression(expr) == result expr = 3*Trace(M) result = CodegenArrayContraction(CodegenArrayTensorProduct(3, M), (0, 1)) assert parse_matrix_expression(expr) == result expr = 3*Trace(Trace(M) * M) result = CodegenArrayContraction(CodegenArrayTensorProduct(3, M, M), (0, 1), (2, 3)) assert parse_matrix_expression(expr) == result expr = 3*Trace(M)**2 result = CodegenArrayContraction(CodegenArrayTensorProduct(3, M, M), (0, 1), (2, 3)) assert parse_matrix_expression(expr) == result expr = HadamardProduct(M, N) result = CodegenArrayDiagonal(CodegenArrayTensorProduct(M, N), (0, 2), (1, 3)) assert parse_matrix_expression(expr) == result expr = HadamardPower(M, 2) result = CodegenArrayDiagonal(CodegenArrayTensorProduct(M, M), (0, 2), (1, 3)) assert parse_matrix_expression(expr) == result expr = M**2 assert isinstance(expr, MatPow) assert parse_matrix_expression(expr) == CodegenArrayContraction(CodegenArrayTensorProduct(M, M), (1, 2))
MATRIX_DIFF_RULES = { # e =expression, s = a list of symbols respsect to which # we want to differentiate Symbol: lambda e, s: d(e) if (e in s) else 0, MatrixSymbol: lambda e, s: d(e) if (e in s) else ZeroMatrix(*e.shape), SymmetricMatrixSymbol: lambda e, s: d(e) if (e in s) else ZeroMatrix(*e.shape), Add: lambda e, s: Add(*[_matDiff_apply(arg, s) for arg in e.args]), Mul: lambda e, s: _matDiff_apply(e.args[0], s) if len(e.args)==1 else Mul(_matDiff_apply(e.args[0],s),Mul(*e.args[1:])) + Mul(e.args[0], _matDiff_apply(Mul(*e.args[1:]),s)), MatAdd: lambda e, s: MatAdd(*[_matDiff_apply(arg, s) for arg in e.args]), MatMul: lambda e, s: _matDiff_apply(e.args[0], s) if len(e.args)==1 else MatMul(_matDiff_apply(e.args[0],s),MatMul(*e.args[1:])) + MatMul(e.args[0], _matDiff_apply(MatMul(*e.args[1:]),s)), Kron: lambda e, s: _matDiff_apply(e.args[0],s) if len(e.args)==1 else Kron(_matDiff_apply(e.args[0],s),Kron(*e.args[1:])) + Kron(e.args[0],_matDiff_apply(Kron(*e.args[1:]),s)), Determinant: lambda e, s: MatMul(Determinant(e.args[0]), Trace(e.args[0].I*_matDiff_apply(e.args[0], s))), # inverse always has 1 arg, so we index Inverse: lambda e, s: -Inverse(e.args[0]) * _matDiff_apply(e.args[0], s) * Inverse(e.args[0]), # trace always has 1 arg Trace: lambda e, s: Trace(_matDiff_apply(e.args[0], s)), # transpose also always has 1 arg, index Transpose: lambda e, s: Transpose(_matDiff_apply(e.args[0], s)) } def _matDiff_apply(expr, syms): if expr.__class__ in list(MATRIX_DIFF_RULES.keys()): return MATRIX_DIFF_RULES[expr.__class__](expr, syms) elif expr.is_constant(): return 0 else: raise TypeError("Don't know how to differentiate class %s", expr.__class__)
def test_arrayexpr_convert_matrix_to_array(): expr = M * N result = ArrayContraction(ArrayTensorProduct(M, N), (1, 2)) assert convert_matrix_to_array(expr) == result expr = M * N * M result = ArrayContraction(ArrayTensorProduct(M, N, M), (1, 2), (3, 4)) assert convert_matrix_to_array(expr) == result expr = Transpose(M) assert convert_matrix_to_array(expr) == PermuteDims(M, [1, 0]) expr = M * Transpose(N) assert convert_matrix_to_array(expr) == ArrayContraction( ArrayTensorProduct(M, PermuteDims(N, [1, 0])), (1, 2)) expr = 3 * M * N res = convert_matrix_to_array(expr) rexpr = convert_array_to_matrix(res) assert expr == rexpr expr = 3 * M + N * M.T * M + 4 * k * N res = convert_matrix_to_array(expr) rexpr = convert_array_to_matrix(res) assert expr == rexpr expr = Inverse(M) * N rexpr = convert_array_to_matrix(convert_matrix_to_array(expr)) assert expr == rexpr expr = M**2 rexpr = convert_array_to_matrix(convert_matrix_to_array(expr)) assert expr == rexpr expr = M * (2 * N + 3 * M) res = convert_matrix_to_array(expr) rexpr = convert_array_to_matrix(res) assert expr == rexpr expr = Trace(M) result = ArrayContraction(M, (0, 1)) assert convert_matrix_to_array(expr) == result expr = 3 * Trace(M) result = ArrayContraction(ArrayTensorProduct(3, M), (0, 1)) assert convert_matrix_to_array(expr) == result expr = 3 * Trace(Trace(M) * M) result = ArrayContraction(ArrayTensorProduct(3, M, M), (0, 1), (2, 3)) assert convert_matrix_to_array(expr) == result expr = 3 * Trace(M)**2 result = ArrayContraction(ArrayTensorProduct(3, M, M), (0, 1), (2, 3)) assert convert_matrix_to_array(expr) == result expr = HadamardProduct(M, N) result = ArrayDiagonal(ArrayTensorProduct(M, N), (0, 2), (1, 3)) assert convert_matrix_to_array(expr) == result expr = HadamardPower(M, 2) result = ArrayDiagonal(ArrayTensorProduct(M, M), (0, 2), (1, 3)) assert convert_matrix_to_array(expr) == result expr = M**2 assert isinstance(expr, MatPow) assert convert_matrix_to_array(expr) == ArrayContraction( ArrayTensorProduct(M, M), (1, 2)) expr = a.T * b cg = convert_matrix_to_array(expr) assert cg == ArrayContraction(ArrayTensorProduct(a, b), (0, 2))
def _eval_inverse(self): inv = Inverse(self) inv.is_Symmetric = True return inv
def test_xxinv(): assert xxinv(MatMul(D, Inverse(D), D, evaluate=False)) == \ MatMul(Identity(n), D, evaluate=False)