123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- """
- This module contains query handlers responsible for Matrices queries:
- Square, Symmetric, Invertible etc.
- """
- from sympy.logic.boolalg import conjuncts
- from sympy.assumptions import Q, ask
- from sympy.assumptions.handlers import test_closed_group
- from sympy.matrices import MatrixBase
- from sympy.matrices.expressions import (BlockMatrix, BlockDiagMatrix, Determinant,
- DiagMatrix, DiagonalMatrix, HadamardProduct, Identity, Inverse, MatAdd, MatMul,
- MatPow, MatrixExpr, MatrixSlice, MatrixSymbol, OneMatrix, Trace, Transpose,
- ZeroMatrix)
- from sympy.matrices.expressions.factorizations import Factorization
- from sympy.matrices.expressions.fourier import DFT
- from sympy.core.logic import fuzzy_and
- from sympy.utilities.iterables import sift
- from sympy.core import Basic
- from ..predicates.matrices import (SquarePredicate, SymmetricPredicate,
- InvertiblePredicate, OrthogonalPredicate, UnitaryPredicate,
- FullRankPredicate, PositiveDefinitePredicate, UpperTriangularPredicate,
- LowerTriangularPredicate, DiagonalPredicate, IntegerElementsPredicate,
- RealElementsPredicate, ComplexElementsPredicate)
- def _Factorization(predicate, expr, assumptions):
- if predicate in expr.predicates:
- return True
- # SquarePredicate
- @SquarePredicate.register(MatrixExpr)
- def _(expr, assumptions):
- return expr.shape[0] == expr.shape[1]
- # SymmetricPredicate
- @SymmetricPredicate.register(MatMul)
- def _(expr, assumptions):
- factor, mmul = expr.as_coeff_mmul()
- if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
- return True
- # TODO: implement sathandlers system for the matrices.
- # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
- if ask(Q.diagonal(expr), assumptions):
- return True
- if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T:
- if len(mmul.args) == 2:
- return True
- return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
- @SymmetricPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.symmetric(base), assumptions)
- return None
- @SymmetricPredicate.register(MatAdd)
- def _(expr, assumptions):
- return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
- @SymmetricPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if not expr.is_square:
- return False
- # TODO: implement sathandlers system for the matrices.
- # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
- if ask(Q.diagonal(expr), assumptions):
- return True
- if Q.symmetric(expr) in conjuncts(assumptions):
- return True
- @SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
- def _(expr, assumptions):
- return ask(Q.square(expr), assumptions)
- @SymmetricPredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.symmetric(expr.arg), assumptions)
- @SymmetricPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- # TODO: implement sathandlers system for the matrices.
- # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
- if ask(Q.diagonal(expr), assumptions):
- return True
- if not expr.on_diag:
- return None
- else:
- return ask(Q.symmetric(expr.parent), assumptions)
- @SymmetricPredicate.register(Identity)
- def _(expr, assumptions):
- return True
- # InvertiblePredicate
- @InvertiblePredicate.register(MatMul)
- def _(expr, assumptions):
- factor, mmul = expr.as_coeff_mmul()
- if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
- return True
- if any(ask(Q.invertible(arg), assumptions) is False
- for arg in mmul.args):
- return False
- @InvertiblePredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- if exp.is_negative == False:
- return ask(Q.invertible(base), assumptions)
- return None
- @InvertiblePredicate.register(MatAdd)
- def _(expr, assumptions):
- return None
- @InvertiblePredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if not expr.is_square:
- return False
- if Q.invertible(expr) in conjuncts(assumptions):
- return True
- @InvertiblePredicate.register_many(Identity, Inverse)
- def _(expr, assumptions):
- return True
- @InvertiblePredicate.register(ZeroMatrix)
- def _(expr, assumptions):
- return False
- @InvertiblePredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @InvertiblePredicate.register(Transpose)
- def _(expr, assumptions):
- return ask(Q.invertible(expr.arg), assumptions)
- @InvertiblePredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.invertible(expr.parent), assumptions)
- @InvertiblePredicate.register(MatrixBase)
- def _(expr, assumptions):
- if not expr.is_square:
- return False
- return expr.rank() == expr.rows
- @InvertiblePredicate.register(MatrixExpr)
- def _(expr, assumptions):
- if not expr.is_square:
- return False
- return None
- @InvertiblePredicate.register(BlockMatrix)
- def _(expr, assumptions):
- from sympy.matrices.expressions.blockmatrix import reblock_2x2
- if not expr.is_square:
- return False
- if expr.blockshape == (1, 1):
- return ask(Q.invertible(expr.blocks[0, 0]), assumptions)
- expr = reblock_2x2(expr)
- if expr.blockshape == (2, 2):
- [[A, B], [C, D]] = expr.blocks.tolist()
- if ask(Q.invertible(A), assumptions) == True:
- invertible = ask(Q.invertible(D - C * A.I * B), assumptions)
- if invertible is not None:
- return invertible
- if ask(Q.invertible(B), assumptions) == True:
- invertible = ask(Q.invertible(C - D * B.I * A), assumptions)
- if invertible is not None:
- return invertible
- if ask(Q.invertible(C), assumptions) == True:
- invertible = ask(Q.invertible(B - A * C.I * D), assumptions)
- if invertible is not None:
- return invertible
- if ask(Q.invertible(D), assumptions) == True:
- invertible = ask(Q.invertible(A - B * D.I * C), assumptions)
- if invertible is not None:
- return invertible
- return None
- @InvertiblePredicate.register(BlockDiagMatrix)
- def _(expr, assumptions):
- if expr.rowblocksizes != expr.colblocksizes:
- return None
- return fuzzy_and([ask(Q.invertible(a), assumptions) for a in expr.diag])
- # OrthogonalPredicate
- @OrthogonalPredicate.register(MatMul)
- def _(expr, assumptions):
- factor, mmul = expr.as_coeff_mmul()
- if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
- factor == 1):
- return True
- if any(ask(Q.invertible(arg), assumptions) is False
- for arg in mmul.args):
- return False
- @OrthogonalPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if int_exp:
- return ask(Q.orthogonal(base), assumptions)
- return None
- @OrthogonalPredicate.register(MatAdd)
- def _(expr, assumptions):
- if (len(expr.args) == 1 and
- ask(Q.orthogonal(expr.args[0]), assumptions)):
- return True
- @OrthogonalPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if (not expr.is_square or
- ask(Q.invertible(expr), assumptions) is False):
- return False
- if Q.orthogonal(expr) in conjuncts(assumptions):
- return True
- @OrthogonalPredicate.register(Identity)
- def _(expr, assumptions):
- return True
- @OrthogonalPredicate.register(ZeroMatrix)
- def _(expr, assumptions):
- return False
- @OrthogonalPredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.orthogonal(expr.arg), assumptions)
- @OrthogonalPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.orthogonal(expr.parent), assumptions)
- @OrthogonalPredicate.register(Factorization)
- def _(expr, assumptions):
- return _Factorization(Q.orthogonal, expr, assumptions)
- # UnitaryPredicate
- @UnitaryPredicate.register(MatMul)
- def _(expr, assumptions):
- factor, mmul = expr.as_coeff_mmul()
- if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
- abs(factor) == 1):
- return True
- if any(ask(Q.invertible(arg), assumptions) is False
- for arg in mmul.args):
- return False
- @UnitaryPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if int_exp:
- return ask(Q.unitary(base), assumptions)
- return None
- @UnitaryPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if (not expr.is_square or
- ask(Q.invertible(expr), assumptions) is False):
- return False
- if Q.unitary(expr) in conjuncts(assumptions):
- return True
- @UnitaryPredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.unitary(expr.arg), assumptions)
- @UnitaryPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.unitary(expr.parent), assumptions)
- @UnitaryPredicate.register_many(DFT, Identity)
- def _(expr, assumptions):
- return True
- @UnitaryPredicate.register(ZeroMatrix)
- def _(expr, assumptions):
- return False
- @UnitaryPredicate.register(Factorization)
- def _(expr, assumptions):
- return _Factorization(Q.unitary, expr, assumptions)
- # FullRankPredicate
- @FullRankPredicate.register(MatMul)
- def _(expr, assumptions):
- if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
- return True
- @FullRankPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if int_exp and ask(~Q.negative(exp), assumptions):
- return ask(Q.fullrank(base), assumptions)
- return None
- @FullRankPredicate.register(Identity)
- def _(expr, assumptions):
- return True
- @FullRankPredicate.register(ZeroMatrix)
- def _(expr, assumptions):
- return False
- @FullRankPredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @FullRankPredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.fullrank(expr.arg), assumptions)
- @FullRankPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if ask(Q.orthogonal(expr.parent), assumptions):
- return True
- # PositiveDefinitePredicate
- @PositiveDefinitePredicate.register(MatMul)
- def _(expr, assumptions):
- factor, mmul = expr.as_coeff_mmul()
- if (all(ask(Q.positive_definite(arg), assumptions)
- for arg in mmul.args) and factor > 0):
- return True
- if (len(mmul.args) >= 2
- and mmul.args[0] == mmul.args[-1].T
- and ask(Q.fullrank(mmul.args[0]), assumptions)):
- return ask(Q.positive_definite(
- MatMul(*mmul.args[1:-1])), assumptions)
- @PositiveDefinitePredicate.register(MatPow)
- def _(expr, assumptions):
- # a power of a positive definite matrix is positive definite
- if ask(Q.positive_definite(expr.args[0]), assumptions):
- return True
- @PositiveDefinitePredicate.register(MatAdd)
- def _(expr, assumptions):
- if all(ask(Q.positive_definite(arg), assumptions)
- for arg in expr.args):
- return True
- @PositiveDefinitePredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if not expr.is_square:
- return False
- if Q.positive_definite(expr) in conjuncts(assumptions):
- return True
- @PositiveDefinitePredicate.register(Identity)
- def _(expr, assumptions):
- return True
- @PositiveDefinitePredicate.register(ZeroMatrix)
- def _(expr, assumptions):
- return False
- @PositiveDefinitePredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @PositiveDefinitePredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.positive_definite(expr.arg), assumptions)
- @PositiveDefinitePredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.positive_definite(expr.parent), assumptions)
- # UpperTriangularPredicate
- @UpperTriangularPredicate.register(MatMul)
- def _(expr, assumptions):
- factor, matrices = expr.as_coeff_matrices()
- if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
- return True
- @UpperTriangularPredicate.register(MatAdd)
- def _(expr, assumptions):
- if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
- return True
- @UpperTriangularPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.upper_triangular(base), assumptions)
- return None
- @UpperTriangularPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if Q.upper_triangular(expr) in conjuncts(assumptions):
- return True
- @UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
- def _(expr, assumptions):
- return True
- @UpperTriangularPredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @UpperTriangularPredicate.register(Transpose)
- def _(expr, assumptions):
- return ask(Q.lower_triangular(expr.arg), assumptions)
- @UpperTriangularPredicate.register(Inverse)
- def _(expr, assumptions):
- return ask(Q.upper_triangular(expr.arg), assumptions)
- @UpperTriangularPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.upper_triangular(expr.parent), assumptions)
- @UpperTriangularPredicate.register(Factorization)
- def _(expr, assumptions):
- return _Factorization(Q.upper_triangular, expr, assumptions)
- # LowerTriangularPredicate
- @LowerTriangularPredicate.register(MatMul)
- def _(expr, assumptions):
- factor, matrices = expr.as_coeff_matrices()
- if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
- return True
- @LowerTriangularPredicate.register(MatAdd)
- def _(expr, assumptions):
- if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
- return True
- @LowerTriangularPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.lower_triangular(base), assumptions)
- return None
- @LowerTriangularPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if Q.lower_triangular(expr) in conjuncts(assumptions):
- return True
- @LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
- def _(expr, assumptions):
- return True
- @LowerTriangularPredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @LowerTriangularPredicate.register(Transpose)
- def _(expr, assumptions):
- return ask(Q.upper_triangular(expr.arg), assumptions)
- @LowerTriangularPredicate.register(Inverse)
- def _(expr, assumptions):
- return ask(Q.lower_triangular(expr.arg), assumptions)
- @LowerTriangularPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if not expr.on_diag:
- return None
- else:
- return ask(Q.lower_triangular(expr.parent), assumptions)
- @LowerTriangularPredicate.register(Factorization)
- def _(expr, assumptions):
- return _Factorization(Q.lower_triangular, expr, assumptions)
- # DiagonalPredicate
- def _is_empty_or_1x1(expr):
- return expr.shape in ((0, 0), (1, 1))
- @DiagonalPredicate.register(MatMul)
- def _(expr, assumptions):
- if _is_empty_or_1x1(expr):
- return True
- factor, matrices = expr.as_coeff_matrices()
- if all(ask(Q.diagonal(m), assumptions) for m in matrices):
- return True
- @DiagonalPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.diagonal(base), assumptions)
- return None
- @DiagonalPredicate.register(MatAdd)
- def _(expr, assumptions):
- if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
- return True
- @DiagonalPredicate.register(MatrixSymbol)
- def _(expr, assumptions):
- if _is_empty_or_1x1(expr):
- return True
- if Q.diagonal(expr) in conjuncts(assumptions):
- return True
- @DiagonalPredicate.register(OneMatrix)
- def _(expr, assumptions):
- return expr.shape[0] == 1 and expr.shape[1] == 1
- @DiagonalPredicate.register_many(Inverse, Transpose)
- def _(expr, assumptions):
- return ask(Q.diagonal(expr.arg), assumptions)
- @DiagonalPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- if _is_empty_or_1x1(expr):
- return True
- if not expr.on_diag:
- return None
- else:
- return ask(Q.diagonal(expr.parent), assumptions)
- @DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
- def _(expr, assumptions):
- return True
- @DiagonalPredicate.register(Factorization)
- def _(expr, assumptions):
- return _Factorization(Q.diagonal, expr, assumptions)
- # IntegerElementsPredicate
- def BM_elements(predicate, expr, assumptions):
- """ Block Matrix elements. """
- return all(ask(predicate(b), assumptions) for b in expr.blocks)
- def MS_elements(predicate, expr, assumptions):
- """ Matrix Slice elements. """
- return ask(predicate(expr.parent), assumptions)
- def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
- d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
- factors, matrices = d[False], d[True]
- return fuzzy_and([
- test_closed_group(Basic(*factors), assumptions, scalar_predicate),
- test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
- @IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
- Trace, Transpose)
- def _(expr, assumptions):
- return test_closed_group(expr, assumptions, Q.integer_elements)
- @IntegerElementsPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- if exp.is_negative == False:
- return ask(Q.integer_elements(base), assumptions)
- return None
- @IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
- def _(expr, assumptions):
- return True
- @IntegerElementsPredicate.register(MatMul)
- def _(expr, assumptions):
- return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
- @IntegerElementsPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- return MS_elements(Q.integer_elements, expr, assumptions)
- @IntegerElementsPredicate.register(BlockMatrix)
- def _(expr, assumptions):
- return BM_elements(Q.integer_elements, expr, assumptions)
- # RealElementsPredicate
- @RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
- MatAdd, Trace, Transpose)
- def _(expr, assumptions):
- return test_closed_group(expr, assumptions, Q.real_elements)
- @RealElementsPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.real_elements(base), assumptions)
- return None
- @RealElementsPredicate.register(MatMul)
- def _(expr, assumptions):
- return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
- @RealElementsPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- return MS_elements(Q.real_elements, expr, assumptions)
- @RealElementsPredicate.register(BlockMatrix)
- def _(expr, assumptions):
- return BM_elements(Q.real_elements, expr, assumptions)
- # ComplexElementsPredicate
- @ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
- Inverse, MatAdd, Trace, Transpose)
- def _(expr, assumptions):
- return test_closed_group(expr, assumptions, Q.complex_elements)
- @ComplexElementsPredicate.register(MatPow)
- def _(expr, assumptions):
- # only for integer powers
- base, exp = expr.args
- int_exp = ask(Q.integer(exp), assumptions)
- if not int_exp:
- return None
- non_negative = ask(~Q.negative(exp), assumptions)
- if (non_negative or non_negative == False
- and ask(Q.invertible(base), assumptions)):
- return ask(Q.complex_elements(base), assumptions)
- return None
- @ComplexElementsPredicate.register(MatMul)
- def _(expr, assumptions):
- return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
- @ComplexElementsPredicate.register(MatrixSlice)
- def _(expr, assumptions):
- return MS_elements(Q.complex_elements, expr, assumptions)
- @ComplexElementsPredicate.register(BlockMatrix)
- def _(expr, assumptions):
- return BM_elements(Q.complex_elements, expr, assumptions)
- @ComplexElementsPredicate.register(DFT)
- def _(expr, assumptions):
- return True
|