Example #1
0
def test_fuzzy_group():
    from sympy.utilities.iterables import cartes
    v = [T, F, U]
    for i in cartes(*[v]*3):
        assert _fuzzy_group(i) is (
            None if None in i else (True if all(j for j in i) else False))
        assert _fuzzy_group(i, quick_exit=True) is (
            None if (i.count(False) > 1) else (None if None in i else (
            True if all(j for j in i) else False)))
Example #2
0
def test_fuzzy_group():
    from sympy.utilities.iterables import cartes
    v = [T, F, U]
    for i in cartes(*[v]*3):
        assert _fuzzy_group(i) is (
            None if None in i else (True if all(j for j in i) else False))
        assert _fuzzy_group(i, quick_exit=True) is (
            None if (i.count(False) > 1) else (None if None in i else (
            True if all(j for j in i) else False)))
Example #3
0
def test_fuzzy_group():
    v = [T, F, U]
    for i in product(*[v]*3):
        assert _fuzzy_group(i) is (None if None in i else
                                   (True if all(j for j in i) else False))
        assert _fuzzy_group(i, quick_exit=True) is \
            (None if (i.count(False) > 1) else
             (None if None in i else (True if all(j for j in i) else False)))
    it = (True if (i == 0) else None for i in range(2))
    assert _torf(it) is None
    it = (True if (i == 1) else None for i in range(2))
    assert _torf(it) is None
Example #4
0
def test_closed_group(expr, assumptions, key):
    """
    Test for membership in a group with respect
    to the current operation
    """
    return _fuzzy_group((ask(key(a), assumptions) for a in expr.args),
                        quick_exit=True)
Example #5
0
def test_closed_group(expr, assumptions, key):
    """
    Test for membership in a group with respect
    to the current operation
    """
    return _fuzzy_group(
        (ask(key(a), assumptions) for a in expr.args), quick_exit=True)
Example #6
0
class BlockMatrix(MatrixExpr):
    @property
    def dtype(self):
        dtype = None
        for arg in self.args:
            _dtype = arg.dtype
            if dtype is None or dtype in _dtype:
                dtype = _dtype
        return dtype

    def __new__(cls, *args, **kwargs):
        if len(args) > 1 and isinstance(args[-1], tuple):
            *args, (axis, ) = args
        elif 'axis' in kwargs:
            axis = kwargs.pop('axis')
        else:
            axis = 0
        _args = []

        if len(args) == 1 and isinstance(args[0], (list, tuple)):
            args = args[0]
            if all(isinstance(arg, (list, tuple)) for arg in args):
                args = [cls(*(x.T for x in arr)).T for arr in args]

        from sympy import sympify
        args = [*map(sympify, args)]
        length = max(len(arg.shape) for arg in args)
        for arg in args:
            if isinstance(arg, BlockMatrix) and len(
                    arg.shape) == length and arg.axis == axis:
                _args += arg.args
            else:
                _args.append(arg)
        if all(not arg.shape for arg in _args):
            return Matrix(tuple(_args))
        blocks = Basic.__new__(cls, *_args, **kwargs)
        blocks.axis = sympify(axis)
        return blocks

    @property
    def kwargs(self):
        # return hyper parameter of this object
        return {'axis': self.axis}

    @staticmethod
    def broadcast(shapes):
        length = 0
        cols = 0
        for i, shape in enumerate(shapes):
            if not shape:
                shapes[i] = (1, )
                shape = shapes[i]
            if len(shape) > 2:
                ...
            else:
                if shape[-1] > cols:
                    cols = shape[-1]
            if len(shape) > length:
                length = len(shape)

        if length == 1 and all(
                shape[0] == shapes[0][0] and len(shape) == length
                for shape in shapes):
            length += 1

        for i, shape in enumerate(shapes):
            if len(shape) > 2:
                ...
            else:
                if shape[-1] < cols and len(shape) > 1:
                    shape = shape[:-1] + (cols, )
            if len(shape) < length:
                shape = (1, ) * (length - len(shape)) + shape
            shapes[i] = shape
        return shapes

    def _eval_shape(self):
        if self.axis:
            shapes = [arg.shape for arg in self.args]
            max_length = {len(s) for s in shapes}
            assert len(max_length) == 1
            max_length, *_ = max_length
            assert self.axis < max_length

            for axis in {*range(max_length)} - {self.axis}:
                if len({s[axis] for s in shapes}) > 1:
                    print([s[axis] for s in shapes])
                assert len({s[axis] for s in shapes}) == 1

            shape = shapes[0]
            dimension_axis = 0
            for s in shapes:
                dimension_axis += s[self.axis]
            return shape[:self.axis] + (dimension_axis, ) + shape[self.axis +
                                                                  1:max_length]
        else:
            shapes = [arg.shape for arg in self.args]
            self.broadcast(shapes)
            rows = sum(s[0] for s in shapes)
            if len(shapes[0]) > 1:
                return (rows, *shapes[0][1:])
            else:
                return (rows, )

    @property
    def shape(self):
        if 'shape' in self._assumptions:
            return self._assumptions['shape']
        shape = self._eval_shape()
        self._assumptions['shape'] = shape
        return shape

    def __getitem__(self, key):
        from sympy.functions.elementary.piecewise import Piecewise
        if isinstance(key, slice):
            start, stop = key.start, key.stop
            if start is None:
                start = 0
            if stop is None:
                stop = self.shape[0]

            if start == 0 and stop == self.shape[0]:
                return self

            rows = 0
            args = []

            len_self_shape = len(self.shape)
            for arg in self.args:
                if start >= stop:
                    break
                index = rows
                if len(arg.shape) < len_self_shape:
                    rows += 1
                else:
                    rows += arg.shape[0]

                if start < rows:
                    if len(arg.shape) < len_self_shape:
                        args.append(arg)
                        start += 1
                    elif rows <= stop:
                        if rows - start < arg.shape[0]:
                            args.append(arg[start:])
                        else:
                            args.append(arg)
                        start = rows
                    else:
                        args.append(arg[start - index:stop - index])
                        start = stop
            if len(args) == 1:
                return args[0]
            if len(args) == 0:
                return ZeroMatrix(*self.shape)
            return self.func(*args)
        if isinstance(key, tuple):
            if len(key) == 1:
                key = key[0]

            elif len(key) == 2:
                i, j = key
                if isinstance(i, slice):
                    if isinstance(j, slice):
                        raise Exception('unimplemented method')
                    else:
                        assert i.step is None, 'unimplemented slice object %s' % i
                        start, stop = i.start, i.stop
                        if start is None:
                            if stop is None:
                                # v have the same columns
                                args = []
                                for v in self.args:
                                    if len(v.shape) > 1:
                                        indexed = v[:, j]
                                    else:
                                        indexed = v[j]
                                    args.append(indexed)
                                return self.func(*args)

                        raise Exception('unimplemented slice object %s' % i)
                elif isinstance(j, slice):
                    raise Exception('unimplemented method')
                from sympy.core.sympify import _sympify
                i, j = _sympify(i), _sympify(j)
                if self.valid_index(i, j) != False:
                    args = []
                    length = 0
                    for arg in self.args:
                        _length = length

                        shape = arg.shape
                        length += shape[0]
                        cond = i < length
                        if len(arg.shape) == 1:
                            args.append([arg[j], cond])
                        else:
                            if cond.is_BooleanFalse:
                                continue
                            args.append([arg[i - _length, j], cond])

                    args[-1][-1] = True
                    return Piecewise(*args)
                else:
                    raise IndexError("Invalid indices (%s, %s)" % (i, j))

        if isinstance(key,
                      int) or key.is_Integer or key.is_Symbol or key.is_Expr:
            if self.axis == 0:
                from sympy import S
                rows = S.Zero
                args = []
                for arg in self.args:
                    index = rows
                    if len(arg.shape) < len(self.shape):
                        rows += S.One
                    else:
                        rows += arg.shape[0]

                    cond = key < rows
                    if cond.is_BooleanFalse:
                        continue

                    if len(arg.shape) < len(self.shape):
                        args.append([arg, cond])
                    else:
                        args.append([arg[key - index], cond])
                args[-1][-1] = True
                return Piecewise(*args)
            else:
                return self.func(*(a[key] for a in self.args),
                                 axis=self.axis - 1)

        raise IndexError("Invalid index, wanted %s[i,j]" % self)

    def _eval_determinant(self):
        from sympy.concrete.products import Product
        if self.is_upper or self.is_lower:
            i = self.generate_var(integer=True)
            return Product(self[i, i], (i, 0, self.cols)).doit()

    @property
    def is_lower(self):
        """Check if matrix is a lower triangular matrix. True can be returned
        even if the matrix is not square.

        Examples
        ========

        >>> from sympy import Matrix
        >>> m = Matrix(2, 2, [1, 0, 0, 1])
        >>> m
        Matrix([
        [1, 0],
        [0, 1]])
        >>> m.is_lower
        True

        >>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4 , 0, 6, 6, 5])
        >>> m
        Matrix([
        [0, 0, 0],
        [2, 0, 0],
        [1, 4, 0],
        [6, 6, 5]])
        >>> m.is_lower
        True

        >>> from sympy.abc import x, y
        >>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y])
        >>> m
        Matrix([
        [x**2 + y, x + y**2],
        [       0,    x + y]])
        >>> m.is_lower
        False

        See Also
        ========

        is_upper
        is_diagonal
        is_lower_hessenberg
        """
        from sympy import Range, Min

        i = self.generate_var(domain=Range(0, Min(self.rows, self.cols - 1)))
        j = i.generate_var(free_symbols=self.free_symbols,
                           domain=Range(i + 1, self.cols))
        assert i < j
        return self[i, j] == 0

    @property
    def is_upper(self):
        """Check if matrix is an upper triangular matrix. True can be returned
        even if the matrix is not square.

        Examples
        ========

        >>> from sympy import Matrix
        >>> m = Matrix(2, 2, [1, 0, 0, 1])
        >>> m
        Matrix([
        [1, 0],
        [0, 1]])
        >>> m.is_upper
        True

        >>> m = Matrix(4, 3, [5, 1, 9, 0, 4 , 6, 0, 0, 5, 0, 0, 0])
        >>> m
        Matrix([
        [5, 1, 9],
        [0, 4, 6],
        [0, 0, 5],
        [0, 0, 0]])
        >>> m.is_upper
        True

        >>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1])
        >>> m
        Matrix([
        [4, 2, 5],
        [6, 1, 1]])
        >>> m.is_upper
        False

        See Also
        ========

        is_lower
        is_diagonal
        is_upper_hessenberg
        """
        from sympy import Range, Min

        j = self.generate_var(domain=Range(0, Min(self.cols, self.rows - 1)))
        i = j.generate_var(free_symbols=self.free_symbols,
                           domain=Range(j + 1, self.rows))
        assert i > j
        return self[i, j] == 0

    def __add__(self, other):
        if isinstance(other, BlockMatrix):
            if len(self.args) == len(other.args):
                if all(x.shape == y.shape
                       for x, y in zip(self.args, other.args)):
                    return self.func(
                        *[x + y for x, y in zip(self.args, other.args)])
        return MatrixExpr.__add__(self, other)

    def simplify(self, deep=False, **kwargs):
        if deep:
            return MatrixExpr.simplify(self, deep=deep, **kwargs)
        if self.axis == 0:
            if self.shape[0] == len(self.args):
                from sympy import Indexed
                start = None
                for i, arg in enumerate(self.args):
                    if not isinstance(arg, Indexed):
                        return self
                    diff = arg.indices[-1] - i
                    if start is None:
                        start = diff
                    else:
                        if start != diff:
                            return self

                return arg.base[start:len(self.args)]

            b = None

            start, stop = None, None
            for arg in self.args:
                if arg.is_Slice or arg.is_Indexed:
                    if b is None:
                        b = arg.base
                    elif b != arg.base or len(arg.indices) > 1:
                        b = None
                        break

                    if start is None:
                        if arg.is_Slice:
                            start, stop = arg.index
                        else:
                            start = arg.index
                            stop = start + 1
                    else:
                        if arg.is_Slice:
                            _start, _stop = arg.index
                        else:
                            _start = arg.index
                            _stop = _start + 1

                        if _start != stop:
                            b = None
                            break
                        stop = _stop
            if b is not None:
                return b[start:stop]
        return self

    @property
    def blocks(self):
        cols = None
        blocks = []
        for X in self.args:
            if X.is_Transpose and X.arg.is_BlockMatrix:
                if cols is None:
                    cols = len(X.arg.args)
                else:
                    if cols != len(X.arg.args):
                        return
                blocks.append([x.T for x in X.arg.args])
                continue
            if len(X.shape) == 1 and X.is_BlockMatrix:
                if cols is None:
                    cols = len(X.args)
                else:
                    if cols != len(X.args):
                        return
                blocks.append([x for x in X.args])
                continue

            return

        for i in range(cols):
            cols = None
            block = [block[i] for block in blocks]
            matrix = [b for b in block if len(b.shape) == 2]

            if matrix:
                shape = matrix[0].shape
                if len(shape) == 2:
                    cols = shape[-1]
                else:
                    cols = shape[-1]

                if any(m.shape[-1] != cols for m in matrix):
                    return

                vector = [b for b in block if len(b.shape) == 1]
                if any(v.shape[0] != cols for v in vector):
                    return

                scalar = [b for b in block if len(b.shape) == 0]
                if scalar:
                    return

        return blocks

    # {c} means center, {l} means left, {r} means right
    def _latex(self, p):
        #         return r'\begin{pmatrix}%s\end{pmatrix}' % r'\\'.join('{%s}' % self._print(arg) for arg in expr.args)

        blocks = self.blocks
        if blocks is not None:
            cols = len(blocks[0])
            array = (' & '.join('{%s}' % p._print(X) for X in block)
                     for block in blocks)
            return r"\left[\begin{array}{%s}%s\end{array}\right]" % (
                'c' * cols, r'\\'.join(array))

        array = []
        for X in self.args:
            if X.is_Transpose and X.arg.is_BlockMatrix:
                X = X.arg
                latex = r"{\left[\begin{array}{%s}%s\end{array}\right]}" % (
                    'c' * len(X.args), ' & '.join('{%s}' % p._print(arg.T)
                                                  for arg in X.args))
            else:
                latex = '{%s}' % p._print(X)
            array.append(latex)

        if len(self.shape) == 1 or self.axis:
            delimiter = ' & '
            center = 'c' * len(self.args)
        else:
            delimiter = r'\\'
            center = 'c'

        latex = r"\left[\begin{array}{%s}%s\end{array}\right]" % (
            center, delimiter.join(array))
        if self.axis:
            latex = "%s_%s" % (latex, p._print(self.axis))
        return latex


#         return r"\begin{equation}\left(\begin{array}{c}%s\end{array}\right)\end{equation}" % r'\\'.join('{%s}' % self._print(arg) for arg in expr.args)

    def _sympystr(self, p):
        tex = r"[%s]" % ','.join(p._print(arg) for arg in self.args)
        if self.axis:
            tex = '%s[%s]' % (tex, self.axis)
        return tex

    def _pretty(self, p):
        return p._print_seq(self.args, '[', ']')

    def _eval_domain_defined(self, x, **_):
        if x.dtype.is_set:
            return x.universalSet

        domain = x.domain
        for arg in self.args:
            domain &= arg.domain_defined(x)
        return domain

    def _eval_transpose(self):
        blocks = self.blocks
        if blocks is None:
            if len(self.shape) == 1:
                return self
            return
        rows = len(blocks)
        cols = len(blocks[0])

        blocks_T = [[None] * rows for _ in range(cols)]
        for i in range(rows):
            for j in range(cols):
                blocks_T[j][i] = blocks[i][j]
        return self.func(*[self.func(*block).T for block in blocks_T])

    def __rmul__(self, other):
        if not other.shape:
            return self.func(*(other * arg for arg in self.args))
        return MatrixExpr.__rmul__(self, other)

    _eval_is_integer = lambda self: _fuzzy_group(
        (a.is_integer for a in self.args), quick_exit=True)

    _eval_is_rational = lambda self: _fuzzy_group(
        (a.is_rational for a in self.args), quick_exit=True)

    _eval_is_extended_real = lambda self: _fuzzy_group(
        (a.is_extended_real for a in self.args), quick_exit=True)

    _eval_is_complex = lambda self: _fuzzy_group(
        (a.is_complex for a in self.args), quick_exit=True)

    _eval_is_extended_positive = lambda self: _fuzzy_group(
        (a.is_extended_positive for a in self.args), quick_exit=True)

    _eval_is_extended_negative = lambda self: _fuzzy_group(
        (a.is_extended_negative for a in self.args), quick_exit=True)

    _eval_is_finite = lambda self: _fuzzy_group(
        (a.is_finite for a in self.args), quick_exit=True)
Example #7
0
class DenseMatrix(MatrixBase):
    __slots__ = []

    is_MatrixExpr = False

    _op_priority = 10.01
    _class_priority = 4

    def __getitem__(self, key):
        """Return portion of self defined by key. If the key involves a slice
        then a list will be returned (if key is a single slice) or a matrix
        (if key was a tuple involving a slice).

        Examples
        ========

        >>> from sympy import Matrix, I
        >>> m = Matrix([
        ... [1, 2 + I],
        ... [3, 4    ]])

        If the key is a tuple that doesn't involve a slice then that element
        is returned:

        >>> m[1, 0]
        3

        When a tuple key involves a slice, a matrix is returned. Here, the
        first column is selected (all rows, column 0):

        >>> m[:, 0]
        Matrix([
        [1],
        [3]])

        If the slice is not a tuple then it selects from the underlying
        list of elements that are arranged in row order and a list is
        returned if a slice is involved:

        >>> m[0]
        1
        >>> m[::2]
        [1, 3]
        """
        if isinstance(key, tuple):
            if len(key) == 1:
                key = key[0]
                return self[key]
            i, j = key
            try:
                i, j = self.key2ij(key)
                return self._args[i * self.cols + j]
            except (TypeError, IndexError):
                if (isinstance(i, Expr)
                        and not i.is_number) or (isinstance(j, Expr)
                                                 and not j.is_number):
                    if ((j < 0) is True) or ((j >= self.shape[1]) is True) or\
                       ((i < 0) is True) or ((i >= self.shape[0]) is True):
                        raise ValueError("index out of boundary")
                    from sympy.matrices.expressions.matexpr import MatrixElement
                    return MatrixElement(self, i, j)

                if isinstance(i, slice):
                    i = range(self.rows)[i]
                elif is_sequence(i):
                    pass
                else:
                    i = [i]
                if isinstance(j, slice):
                    j = range(self.cols)[j]
                elif is_sequence(j):
                    pass
                else:
                    j = [j]
                return self.extract(i, j)
        else:
            # row-wise decomposition of matrix
            if isinstance(key, slice):
                return self._new(self._args[key])
            if len(self.shape) == 1:
                from sympy.functions.elementary.piecewise import Piecewise
                from sympy import Equal
                args = []
                for i in range(len(self._args)):
                    args.append([self._args[i], Equal(key, i)])
                args[-1][1] = True
                return Piecewise(*args).simplify()
            return self._args[a2idx(key)]

    def __setitem__(self, key, value):
        raise NotImplementedError()

    def _cholesky(self, hermitian=True):
        """Helper function of cholesky.
        Without the error checks.
        To be used privately.
        Implements the Cholesky-Banachiewicz algorithm.
        Returns L such that L*L.H == self if hermitian flag is True,
        or L*L.T == self if hermitian is False.
        """
        L = zeros(self.rows, self.rows)
        if hermitian:
            for i in range(self.rows):
                for j in range(i):
                    L[i, j] = (1 / L[j, j]) * expand_mul(self[i, j] - sum(
                        L[i, k] * L[j, k].conjugate() for k in range(j)))
                Lii2 = expand_mul(self[i, i] -
                                  sum(L[i, k] * L[i, k].conjugate()
                                      for k in range(i)))
                if Lii2.is_positive == False:
                    raise ValueError("Matrix must be positive-definite")
                L[i, i] = sqrt(Lii2)
        else:
            for i in range(self.rows):
                for j in range(i):
                    L[i, j] = (1 / L[j, j]) * (self[i, j] -
                                               sum(L[i, k] * L[j, k]
                                                   for k in range(j)))
                L[i, i] = sqrt(self[i, i] - sum(L[i, k]**2 for k in range(i)))
        return self._new(L)

    def _diagonal_solve(self, rhs):
        """Helper function of function diagonal_solve,
        without the error checks, to be used privately.
        """
        return self._new(rhs.rows, rhs.cols,
                         lambda i, j: rhs[i, j] / self[i, i])

    def _eval_add(self, other):
        # we assume both arguments are dense matrices since
        # sparse matrices have a higher priority
        mat = [a + b for a, b in zip(self._args, other._args)]
        return classof(self, other)._new(self.rows, self.cols, mat, copy=False)

    def _eval_extract(self, rowsList, colsList):
        mat = self._args
        cols = self.cols
        indices = (i * cols + j for i in rowsList for j in colsList)
        return self._new(len(rowsList),
                         len(colsList),
                         list(mat[i] for i in indices),
                         copy=False)

    def _eval_matrix_mul(self, other):
        from sympy import Add
        # cache attributes for faster access
        self_rows, self_cols = self.rows, self.cols
        other_rows, other_cols = other.rows, other.cols
        other_len = other_rows * other_cols
        new_mat_rows = self_rows
        if other.rows == 1:
            new_mat_cols = other.rows
            other_rows, other_cols = other_cols, other_rows
        else:
            new_mat_cols = other.cols

        # preallocate the array
        new_mat = [self.zero] * new_mat_rows * new_mat_cols

        # if we multiply an n x 0 with a 0 x m, the
        # expected behavior is to produce an n x m matrix of zeros
        if self_cols != 0 and other_rows != 0:
            # cache self._args and other._args for performance
            mat = self._args
            other_mat = other._args
            for i in range(len(new_mat)):
                row, col = i // new_mat_cols, i % new_mat_cols
                row_indices = range(self_cols * row, self_cols * (row + 1))
                col_indices = range(col, other_len, other_cols)
                vec = (mat[a] * other_mat[b]
                       for a, b in zip(row_indices, col_indices))
                try:
                    new_mat[i] = Add(*vec)
                except (TypeError, SympifyError):
                    # Block matrices don't work with `sum` or `Add` (ISSUE #11599)
                    # They don't work with `sum` because `sum` tries to add `0`
                    # initially, and for a matrix, that is a mix of a scalar and
                    # a matrix, which raises a TypeError. Fall back to a
                    # block-matrix-safe way to multiply if the `sum` fails.
                    vec = (mat[a] * other_mat[b]
                           for a, b in zip(row_indices, col_indices))
                    new_mat[i] = reduce(lambda a, b: a + b, vec)
        return classof(self, other)._new(new_mat_rows,
                                         new_mat_cols,
                                         new_mat,
                                         copy=False)

    def _eval_matrix_mul_elementwise(self, other):
        mat = [a * b for a, b in zip(self._args, other._args)]
        return classof(self, other)._new(self.rows, self.cols, mat, copy=False)

    def _eval_inverse(self, **kwargs):
        """Return the matrix inverse using the method indicated (default
        is Gauss elimination).

        kwargs
        ======

        method : ('GE', 'LU', or 'ADJ')
        iszerofunc
        try_block_diag

        Notes
        =====

        According to the ``method`` keyword, it calls the appropriate method:

          GE .... inverse_GE(); default
          LU .... inverse_LU()
          ADJ ... inverse_ADJ()

        According to the ``try_block_diag`` keyword, it will try to form block
        diagonal matrices using the method get_diag_blocks(), invert these
        individually, and then reconstruct the full inverse matrix.

        Note, the GE and LU methods may require the matrix to be simplified
        before it is inverted in order to properly detect zeros during
        pivoting. In difficult cases a custom zero detection function can
        be provided by setting the ``iszerosfunc`` argument to a function that
        should return True if its argument is zero. The ADJ routine computes
        the determinant and uses that to detect singular matrices in addition
        to testing for zeros on the diagonal.

        See Also
        ========

        inverse_LU
        inverse_GE
        inverse_ADJ
        """
        from sympy.matrices import diag

        method = kwargs.get('method', 'GE')
        iszerofunc = kwargs.get('iszerofunc', _iszero)
        if kwargs.get('try_block_diag', False):
            blocks = self.get_diag_blocks()
            r = []
            for block in blocks:
                r.append(block.inv(method=method, iszerofunc=iszerofunc))
            return diag(*r)

        M = self.as_mutable()
        if method == "GE":
            rv = M.inverse_GE(iszerofunc=iszerofunc)
        elif method == "LU":
            rv = M.inverse_LU(iszerofunc=iszerofunc)
        elif method == "ADJ":
            rv = M.inverse_ADJ(iszerofunc=iszerofunc)
        else:
            # make sure to add an invertibility check (as in inverse_LU)
            # if a new method is added.
            raise ValueError("Inversion method unrecognized")
        return self._new(rv)

    def _eval_scalar_mul(self, other):
        mat = tuple(other * a for a in self._args)
        return self._new(self.rows, self.cols, mat, copy=False)

    def _eval_scalar_rmul(self, other):
        mat = tuple(a * other for a in self._args)
        return self._new(self.rows, self.cols, mat, copy=False)

    def _eval_tolist(self):
        mat = list(self._args)
        cols = self.cols
        return [mat[i * cols:(i + 1) * cols] for i in range(self.rows)]

    def _LDLdecomposition(self, hermitian=True):
        """Helper function of LDLdecomposition.
        Without the error checks.
        To be used privately.
        Returns L and D such that L*D*L.H == self if hermitian flag is True,
        or L*D*L.T == self if hermitian is False.
        """
        # https://en.wikipedia.org/wiki/Cholesky_decomposition#LDL_decomposition_2
        D = zeros(self.rows, self.rows)
        L = eye(self.rows)
        if hermitian:
            for i in range(self.rows):
                for j in range(i):
                    L[i, j] = (1 / D[j, j]) * expand_mul(self[i, j] - sum(
                        L[i, k] * L[j, k].conjugate() * D[k, k]
                        for k in range(j)))
                D[i,
                  i] = expand_mul(self[i, i] -
                                  sum(L[i, k] * L[i, k].conjugate() * D[k, k]
                                      for k in range(i)))
                if D[i, i].is_positive == False:
                    raise ValueError("Matrix must be positive-definite")
        else:
            for i in range(self.rows):
                for j in range(i):
                    L[i, j] = (1 / D[j, j]) * (self[i, j] -
                                               sum(L[i, k] * L[j, k] * D[k, k]
                                                   for k in range(j)))
                D[i,
                  i] = self[i, i] - sum(L[i, k]**2 * D[k, k] for k in range(i))
        return self._new(L), self._new(D)

    def _lower_triangular_solve(self, rhs):
        """Helper function of function lower_triangular_solve.
        Without the error checks.
        To be used privately.
        """
        X = zeros(self.rows, rhs.cols)
        for j in range(rhs.cols):
            for i in range(self.rows):
                if self[i, i] == 0:
                    raise TypeError("Matrix must be non-singular.")
                X[i, j] = (rhs[i, j] - sum(self[i, k] * X[k, j]
                                           for k in range(i))) / self[i, i]
        return self._new(X)

    def _upper_triangular_solve(self, rhs):
        """Helper function of function upper_triangular_solve.
        Without the error checks, to be used privately. """
        X = zeros(self.rows, rhs.cols)
        for j in range(rhs.cols):
            for i in reversed(range(self.rows)):
                if self[i, i] == 0:
                    raise ValueError("Matrix must be non-singular.")
                X[i, j] = (rhs[i, j] -
                           sum(self[i, k] * X[k, j]
                               for k in range(i + 1, self.rows))) / self[i, i]
        return self._new(X)

    def as_immutable(self):
        """Returns an Immutable version of this Matrix
        """
        from .immutable import ImmutableDenseMatrix as cls
        if self.rows and self.cols:
            return cls._new(self.tolist())
        return cls._new(self.rows, self.cols, [])

    def as_mutable(self):
        """Returns a mutable version of this matrix

        Examples
        ========

        >>> from sympy import ImmutableMatrix
        >>> X = ImmutableMatrix([[1, 2], [3, 4]])
        >>> Y = X.as_mutable()
        >>> Y[1, 1] = 5 # Can set values in Y
        >>> Y
        Matrix([
        [1, 2],
        [3, 5]])
        """
        return Matrix(self)

    def equals(self, other, failing_expression=False):
        """Applies ``equals`` to corresponding elements of the matrices,
        trying to prove that the elements are equivalent, returning True
        if they are, False if any pair is not, and None (or the first
        failing expression if failing_expression is True) if it cannot
        be decided if the expressions are equivalent or not. This is, in
        general, an expensive operation.

        Examples
        ========

        >>> from sympy.matrices import Matrix
        >>> from sympy.abc import x
        >>> from sympy import cos
        >>> A = Matrix([x*(x - 1), 0])
        >>> B = Matrix([x**2 - x, 0])
        >>> A == B
        False
        >>> A.simplify() == B.simplify()
        True
        >>> A.equals(B)
        True
        >>> A.equals(2)
        False

        See Also
        ========
        sympy.core.expr.equals
        """
        self_shape = getattr(self, 'shape', None)
        other_shape = getattr(other, 'shape', None)
        if None in (self_shape, other_shape):
            return False
        if self_shape != other_shape:
            return False
        rv = True
        for i in range(self.rows):
            for j in range(self.cols):
                ans = self[i, j].equals(other[i, j], failing_expression)
                if ans is False:
                    return False
                elif ans is not True and rv is True:
                    rv = ans
        return rv

    @property
    def dtype(self):
        dtype = None
        for arg in self._args:
            _dtype = arg.dtype
            if dtype is None or dtype in _dtype:
                dtype = _dtype
        return dtype

    @property
    def domain(self):
        from sympy import Interval, Range, oo, CartesianSpace
        shape = self.shape
        if self.is_integer:
            if self.is_positive:
                interval = Range(1, oo)
            elif self.is_nonnegative:
                interval = Range(0, oo)
            elif self.is_negative:
                interval = Range(-oo, 0)
            elif self.is_nonpositive:
                interval = Range(-oo, 1)
            else:
                interval = Range(-oo, oo)
        elif self.is_extended_real:
            if self.is_positive:
                interval = Interval(0, oo, left_open=True)
            elif self.is_nonnegative:
                interval = Interval(0, oo)
            elif self.is_negative:
                interval = Interval(-oo, 0, right_open=True)
            elif self.is_nonpositive:
                interval = Interval(-oo, 0)
            else:
                interval = Interval(-oo, oo)
        else:
            interval = S.Complexes
        return CartesianSpace(interval, *shape)

    def split(self, indices):
        return self.slice(indices, 0, self.shape[0])

    _eval_is_complex = lambda self: _fuzzy_group(
        (a.is_complex for a in self._args), quick_exit=True)
    _eval_is_finite = lambda self: _fuzzy_group(
        (a.is_finite for a in self._args), quick_exit=True)
    _eval_is_integer = lambda self: _fuzzy_group(
        (a.is_integer for a in self._args), quick_exit=True)
    _eval_is_extended_real = lambda self: _fuzzy_group(
        (a.is_extended_real for a in self._args), quick_exit=True)
    _eval_is_extended_positive = lambda self: _fuzzy_group(
        (a.is_extended_positive for a in self._args), quick_exit=True)
    _eval_is_extended_negative = lambda self: _fuzzy_group(
        (a.is_extended_negative for a in self._args), quick_exit=True)