'Compute an lu decomposition of m x n matrix A, where P*A = L*U
* L is m x m lower triangular with unit diagonal
* U is m x n upper triangular
* P is an m x m permutation matrix
Returns an m x n matrix lu, and an m element list perm where each
element of perm is a pair of row exchange indices.
The factors L and U are stored in lu as follows:
The subdiagonal elements of L are stored in the subdiagonal elements
of lu, that is lu[i, j] = L[i, j] whenever i > j.
The elements on the diagonal of L are all 1, and are not explicitly
stored.
U is stored in the upper triangular portion of lu, that is
lu[i ,j] = U[i, j] whenever i <= j.
The output matrix can be visualized as:
Matrix([
[u, u, u, u],
[l, u, u, u],
[l, l, u, u],
[l, l, l, u]])
where l represents a subdiagonal entry of the L factor, and u
represents an entry from the upper triangular entry of the U
factor.
perm is a list row swap index pairs such that if A is the original
matrix, then A = (L*U).permuteBkwd(perm), and the row permutation
matrix P such that ``P*A = L*U`` can be computed by
``P=eye(A.row).permuteFwd(perm)``.
The keyword argument rankcheck determines if this function raises a
ValueError when passed a matrix whose rank is strictly less than
min(num rows, num cols). The default behavior is to decompose a rank
deficient matrix. Pass rankcheck=True to raise a
ValueError instead. (This mimics the previous behavior of this function).
The keyword arguments iszerofunc and simpfunc are used by the pivot
search algorithm.
iszerofunc is a callable that returns a boolean indicating if its
input is zero, or None if it cannot make the determination.
simpfunc is a callable that simplifies its input.
The default is simpfunc=None, which indicate that the pivot search
algorithm should not attempt to simplify any candidate pivots.
If simpfunc fails to simplify its input, then it must return its input
instead of a copy.
When a matrix contains symbolic entries, the pivot search algorithm
differs from the case where every entry can be categorized as zero or
nonzero.
The algorithm searches column by column through the submatrix whose
top left entry coincides with the pivot position.
If it exists, the pivot is the first entry in the current search
column that iszerofunc guarantees is nonzero.
If no such candidate exists, then each candidate pivot is simplified
if simpfunc is not None.
The search is repeated, with the difference that a candidate may be
the pivot if ``iszerofunc()`` cannot guarantee that it is nonzero.
In the second search the pivot is the first candidate that
iszerofunc can guarantee is nonzero.
If no such candidate exists, then the pivot is the first candidate
for which iszerofunc returns None.
If no such candidate exists, then the search is repeated in the next
column to the right.
The pivot search algorithm differs from the one in `rref()`, which
relies on ``_find_reasonable_pivot()``.
Future versions of ``LUdecomposition_simple()`` may use
``_find_reasonable_pivot()``.
See Also
LUdecomposition
LUdecompositionFF
LUsolve'

def LUdecomposition_Simple(self, iszerofunc=_iszero, simpfunc=None, rankcheck=False):

    if rankcheck:
        pass
    if ((self.rows == 0) or (self.cols == 0)):
        return (self.zeros(self.rows, self.cols), [])
    lu = self.as_mutable()
    row_swaps = []
    pivot_col = 0
    for pivot_row in range(0, (lu.rows - 1)):
        iszeropivot = True
        while ((pivot_col != self.cols) and iszeropivot):
            sub_col = (lu[(r, pivot_col)] for r in range(pivot_row, self.rows))
            (pivot_row_offset, pivot_value, is_assumed_non_zero, ind_simplified_pairs) = _find_reasonable_pivot_naive(sub_col, iszerofunc, simpfunc)
            iszeropivot = (pivot_value is None)
            if iszeropivot:
                pivot_col += 1
        if (rankcheck and (pivot_col != pivot_row)):
            raise ValueError('Rank    of    matrix    is    strictly    less    than    number    of    rows    or    columns.    Pass    keyword    argument    rankcheck=False    to    compute    the    LU    decomposition    of    this    matrix.')
        candidate_pivot_row = (None if (pivot_row_offset is None) else (pivot_row + pivot_row_offset))
        if ((candidate_pivot_row is None) and iszeropivot):
            return (lu, row_swaps)
        for (offset, val) in ind_simplified_pairs:
            lu[((pivot_row + offset), pivot_col)] = val
        if (pivot_row != candidate_pivot_row):
            row_swaps.append([pivot_row, candidate_pivot_row])
            (lu[pivot_row, 0:pivot_row], lu[candidate_pivot_row, 0:pivot_row]) = (lu[candidate_pivot_row, 0:pivot_row], lu[pivot_row, 0:pivot_row])
            (lu[pivot_row, pivot_col:lu.cols], lu[candidate_pivot_row, pivot_col:lu.cols]) = (lu[candidate_pivot_row, pivot_col:lu.cols], lu[pivot_row, pivot_col:lu.cols])
        start_col = (pivot_col + 1)
        for row in range((pivot_row + 1), lu.rows):
            lu[(row, pivot_row)] = (lu[(row, pivot_col)] / lu[(pivot_row, pivot_col)])
            for c in range(start_col, lu.cols):
                lu[(row, c)] = (lu[(row, c)] - (lu[(row, pivot_row)] * lu[(pivot_row, c)]))
        if (pivot_row != pivot_col):
            for row in range((pivot_row + 1), lu.rows):
                lu[(row, pivot_col)] = S.Zero
        pivot_col += 1
        if (pivot_col == lu.cols):
            return (lu, row_swaps)
    return (lu, row_swaps)
