chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
from types import FunctionType
|
||||
|
||||
from sympy.polys.polyerrors import CoercionFailed
|
||||
from sympy.polys.domains import ZZ, QQ
|
||||
|
||||
from .utilities import _get_intermediate_simp, _iszero, _dotprodsimp, _simplify
|
||||
from .determinant import _find_reasonable_pivot
|
||||
|
||||
|
||||
def _row_reduce_list(mat, rows, cols, one, iszerofunc, simpfunc,
|
||||
normalize_last=True, normalize=True, zero_above=True):
|
||||
"""Row reduce a flat list representation of a matrix and return a tuple
|
||||
(rref_matrix, pivot_cols, swaps) where ``rref_matrix`` is a flat list,
|
||||
``pivot_cols`` are the pivot columns and ``swaps`` are any row swaps that
|
||||
were used in the process of row reduction.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
mat : list
|
||||
list of matrix elements, must be ``rows`` * ``cols`` in length
|
||||
|
||||
rows, cols : integer
|
||||
number of rows and columns in flat list representation
|
||||
|
||||
one : SymPy object
|
||||
represents the value one, from ``Matrix.one``
|
||||
|
||||
iszerofunc : determines if an entry can be used as a pivot
|
||||
|
||||
simpfunc : used to simplify elements and test if they are
|
||||
zero if ``iszerofunc`` returns `None`
|
||||
|
||||
normalize_last : indicates where all row reduction should
|
||||
happen in a fraction-free manner and then the rows are
|
||||
normalized (so that the pivots are 1), or whether
|
||||
rows should be normalized along the way (like the naive
|
||||
row reduction algorithm)
|
||||
|
||||
normalize : whether pivot rows should be normalized so that
|
||||
the pivot value is 1
|
||||
|
||||
zero_above : whether entries above the pivot should be zeroed.
|
||||
If ``zero_above=False``, an echelon matrix will be returned.
|
||||
"""
|
||||
|
||||
def get_col(i):
|
||||
return mat[i::cols]
|
||||
|
||||
def row_swap(i, j):
|
||||
mat[i*cols:(i + 1)*cols], mat[j*cols:(j + 1)*cols] = \
|
||||
mat[j*cols:(j + 1)*cols], mat[i*cols:(i + 1)*cols]
|
||||
|
||||
def cross_cancel(a, i, b, j):
|
||||
"""Does the row op row[i] = a*row[i] - b*row[j]"""
|
||||
q = (j - i)*cols
|
||||
for p in range(i*cols, (i + 1)*cols):
|
||||
mat[p] = isimp(a*mat[p] - b*mat[p + q])
|
||||
|
||||
isimp = _get_intermediate_simp(_dotprodsimp)
|
||||
piv_row, piv_col = 0, 0
|
||||
pivot_cols = []
|
||||
swaps = []
|
||||
|
||||
# use a fraction free method to zero above and below each pivot
|
||||
while piv_col < cols and piv_row < rows:
|
||||
pivot_offset, pivot_val, \
|
||||
assumed_nonzero, newly_determined = _find_reasonable_pivot(
|
||||
get_col(piv_col)[piv_row:], iszerofunc, simpfunc)
|
||||
|
||||
# _find_reasonable_pivot may have simplified some things
|
||||
# in the process. Let's not let them go to waste
|
||||
for (offset, val) in newly_determined:
|
||||
offset += piv_row
|
||||
mat[offset*cols + piv_col] = val
|
||||
|
||||
if pivot_offset is None:
|
||||
piv_col += 1
|
||||
continue
|
||||
|
||||
pivot_cols.append(piv_col)
|
||||
if pivot_offset != 0:
|
||||
row_swap(piv_row, pivot_offset + piv_row)
|
||||
swaps.append((piv_row, pivot_offset + piv_row))
|
||||
|
||||
# if we aren't normalizing last, we normalize
|
||||
# before we zero the other rows
|
||||
if normalize_last is False:
|
||||
i, j = piv_row, piv_col
|
||||
mat[i*cols + j] = one
|
||||
for p in range(i*cols + j + 1, (i + 1)*cols):
|
||||
mat[p] = isimp(mat[p] / pivot_val)
|
||||
# after normalizing, the pivot value is 1
|
||||
pivot_val = one
|
||||
|
||||
# zero above and below the pivot
|
||||
for row in range(rows):
|
||||
# don't zero our current row
|
||||
if row == piv_row:
|
||||
continue
|
||||
# don't zero above the pivot unless we're told.
|
||||
if zero_above is False and row < piv_row:
|
||||
continue
|
||||
# if we're already a zero, don't do anything
|
||||
val = mat[row*cols + piv_col]
|
||||
if iszerofunc(val):
|
||||
continue
|
||||
|
||||
cross_cancel(pivot_val, row, val, piv_row)
|
||||
piv_row += 1
|
||||
|
||||
# normalize each row
|
||||
if normalize_last is True and normalize is True:
|
||||
for piv_i, piv_j in enumerate(pivot_cols):
|
||||
pivot_val = mat[piv_i*cols + piv_j]
|
||||
mat[piv_i*cols + piv_j] = one
|
||||
for p in range(piv_i*cols + piv_j + 1, (piv_i + 1)*cols):
|
||||
mat[p] = isimp(mat[p] / pivot_val)
|
||||
|
||||
return mat, tuple(pivot_cols), tuple(swaps)
|
||||
|
||||
|
||||
# This functions is a candidate for caching if it gets implemented for matrices.
|
||||
def _row_reduce(M, iszerofunc, simpfunc, normalize_last=True,
|
||||
normalize=True, zero_above=True):
|
||||
|
||||
mat, pivot_cols, swaps = _row_reduce_list(list(M), M.rows, M.cols, M.one,
|
||||
iszerofunc, simpfunc, normalize_last=normalize_last,
|
||||
normalize=normalize, zero_above=zero_above)
|
||||
|
||||
return M._new(M.rows, M.cols, mat), pivot_cols, swaps
|
||||
|
||||
|
||||
def _is_echelon(M, iszerofunc=_iszero):
|
||||
"""Returns `True` if the matrix is in echelon form. That is, all rows of
|
||||
zeros are at the bottom, and below each leading non-zero in a row are
|
||||
exclusively zeros."""
|
||||
|
||||
if M.rows <= 0 or M.cols <= 0:
|
||||
return True
|
||||
|
||||
zeros_below = all(iszerofunc(t) for t in M[1:, 0])
|
||||
|
||||
if iszerofunc(M[0, 0]):
|
||||
return zeros_below and _is_echelon(M[:, 1:], iszerofunc)
|
||||
|
||||
return zeros_below and _is_echelon(M[1:, 1:], iszerofunc)
|
||||
|
||||
|
||||
def _echelon_form(M, iszerofunc=_iszero, simplify=False, with_pivots=False):
|
||||
"""Returns a matrix row-equivalent to ``M`` that is in echelon form. Note
|
||||
that echelon form of a matrix is *not* unique, however, properties like the
|
||||
row space and the null space are preserved.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Matrix
|
||||
>>> M = Matrix([[1, 2], [3, 4]])
|
||||
>>> M.echelon_form()
|
||||
Matrix([
|
||||
[1, 2],
|
||||
[0, -2]])
|
||||
"""
|
||||
|
||||
simpfunc = simplify if isinstance(simplify, FunctionType) else _simplify
|
||||
|
||||
mat, pivots, _ = _row_reduce(M, iszerofunc, simpfunc,
|
||||
normalize_last=True, normalize=False, zero_above=False)
|
||||
|
||||
if with_pivots:
|
||||
return mat, pivots
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
# This functions is a candidate for caching if it gets implemented for matrices.
|
||||
def _rank(M, iszerofunc=_iszero, simplify=False):
|
||||
"""Returns the rank of a matrix.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Matrix
|
||||
>>> from sympy.abc import x
|
||||
>>> m = Matrix([[1, 2], [x, 1 - 1/x]])
|
||||
>>> m.rank()
|
||||
2
|
||||
>>> n = Matrix(3, 3, range(1, 10))
|
||||
>>> n.rank()
|
||||
2
|
||||
"""
|
||||
|
||||
def _permute_complexity_right(M, iszerofunc):
|
||||
"""Permute columns with complicated elements as
|
||||
far right as they can go. Since the ``sympy`` row reduction
|
||||
algorithms start on the left, having complexity right-shifted
|
||||
speeds things up.
|
||||
|
||||
Returns a tuple (mat, perm) where perm is a permutation
|
||||
of the columns to perform to shift the complex columns right, and mat
|
||||
is the permuted matrix."""
|
||||
|
||||
def complexity(i):
|
||||
# the complexity of a column will be judged by how many
|
||||
# element's zero-ness cannot be determined
|
||||
return sum(1 if iszerofunc(e) is None else 0 for e in M[:, i])
|
||||
|
||||
complex = [(complexity(i), i) for i in range(M.cols)]
|
||||
perm = [j for (i, j) in sorted(complex)]
|
||||
|
||||
return (M.permute(perm, orientation='cols'), perm)
|
||||
|
||||
simpfunc = simplify if isinstance(simplify, FunctionType) else _simplify
|
||||
|
||||
# for small matrices, we compute the rank explicitly
|
||||
# if is_zero on elements doesn't answer the question
|
||||
# for small matrices, we fall back to the full routine.
|
||||
if M.rows <= 0 or M.cols <= 0:
|
||||
return 0
|
||||
|
||||
if M.rows <= 1 or M.cols <= 1:
|
||||
zeros = [iszerofunc(x) for x in M]
|
||||
|
||||
if False in zeros:
|
||||
return 1
|
||||
|
||||
if M.rows == 2 and M.cols == 2:
|
||||
zeros = [iszerofunc(x) for x in M]
|
||||
|
||||
if False not in zeros and None not in zeros:
|
||||
return 0
|
||||
|
||||
d = M.det()
|
||||
|
||||
if iszerofunc(d) and False in zeros:
|
||||
return 1
|
||||
if iszerofunc(d) is False:
|
||||
return 2
|
||||
|
||||
mat, _ = _permute_complexity_right(M, iszerofunc=iszerofunc)
|
||||
_, pivots, _ = _row_reduce(mat, iszerofunc, simpfunc, normalize_last=True,
|
||||
normalize=False, zero_above=False)
|
||||
|
||||
return len(pivots)
|
||||
|
||||
|
||||
def _to_DM_ZZ_QQ(M):
|
||||
# We have to test for _rep here because there are tests that otherwise fail
|
||||
# with e.g. "AttributeError: 'SubspaceOnlyMatrix' object has no attribute
|
||||
# '_rep'." There is almost certainly no value in such tests. The
|
||||
# presumption seems to be that someone could create a new class by
|
||||
# inheriting some of the Matrix classes and not the full set that is used
|
||||
# by the standard Matrix class but if anyone tried that it would fail in
|
||||
# many ways.
|
||||
if not hasattr(M, '_rep'):
|
||||
return None
|
||||
|
||||
rep = M._rep
|
||||
K = rep.domain
|
||||
|
||||
if K.is_ZZ:
|
||||
return rep
|
||||
elif K.is_QQ:
|
||||
try:
|
||||
return rep.convert_to(ZZ)
|
||||
except CoercionFailed:
|
||||
return rep
|
||||
else:
|
||||
if not all(e.is_Rational for e in M):
|
||||
return None
|
||||
try:
|
||||
return rep.convert_to(ZZ)
|
||||
except CoercionFailed:
|
||||
return rep.convert_to(QQ)
|
||||
|
||||
|
||||
def _rref_dm(dM):
|
||||
"""Compute the reduced row echelon form of a DomainMatrix."""
|
||||
K = dM.domain
|
||||
|
||||
if K.is_ZZ:
|
||||
dM_rref, den, pivots = dM.rref_den(keep_domain=False)
|
||||
dM_rref = dM_rref.to_field() / den
|
||||
elif K.is_QQ:
|
||||
dM_rref, pivots = dM.rref()
|
||||
else:
|
||||
assert False # pragma: no cover
|
||||
|
||||
M_rref = dM_rref.to_Matrix()
|
||||
|
||||
return M_rref, pivots
|
||||
|
||||
|
||||
def _rref(M, iszerofunc=_iszero, simplify=False, pivots=True,
|
||||
normalize_last=True):
|
||||
"""Return reduced row-echelon form of matrix and indices
|
||||
of pivot vars.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
iszerofunc : Function
|
||||
A function used for detecting whether an element can
|
||||
act as a pivot. ``lambda x: x.is_zero`` is used by default.
|
||||
|
||||
simplify : Function
|
||||
A function used to simplify elements when looking for a pivot.
|
||||
By default SymPy's ``simplify`` is used.
|
||||
|
||||
pivots : True or False
|
||||
If ``True``, a tuple containing the row-reduced matrix and a tuple
|
||||
of pivot columns is returned. If ``False`` just the row-reduced
|
||||
matrix is returned.
|
||||
|
||||
normalize_last : True or False
|
||||
If ``True``, no pivots are normalized to `1` until after all
|
||||
entries above and below each pivot are zeroed. This means the row
|
||||
reduction algorithm is fraction free until the very last step.
|
||||
If ``False``, the naive row reduction procedure is used where
|
||||
each pivot is normalized to be `1` before row operations are
|
||||
used to zero above and below the pivot.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Matrix
|
||||
>>> from sympy.abc import x
|
||||
>>> m = Matrix([[1, 2], [x, 1 - 1/x]])
|
||||
>>> m.rref()
|
||||
(Matrix([
|
||||
[1, 0],
|
||||
[0, 1]]), (0, 1))
|
||||
>>> rref_matrix, rref_pivots = m.rref()
|
||||
>>> rref_matrix
|
||||
Matrix([
|
||||
[1, 0],
|
||||
[0, 1]])
|
||||
>>> rref_pivots
|
||||
(0, 1)
|
||||
|
||||
``iszerofunc`` can correct rounding errors in matrices with float
|
||||
values. In the following example, calling ``rref()`` leads to
|
||||
floating point errors, incorrectly row reducing the matrix.
|
||||
``iszerofunc= lambda x: abs(x) < 1e-9`` sets sufficiently small numbers
|
||||
to zero, avoiding this error.
|
||||
|
||||
>>> m = Matrix([[0.9, -0.1, -0.2, 0], [-0.8, 0.9, -0.4, 0], [-0.1, -0.8, 0.6, 0]])
|
||||
>>> m.rref()
|
||||
(Matrix([
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 0],
|
||||
[0, 0, 1, 0]]), (0, 1, 2))
|
||||
>>> m.rref(iszerofunc=lambda x:abs(x)<1e-9)
|
||||
(Matrix([
|
||||
[1, 0, -0.301369863013699, 0],
|
||||
[0, 1, -0.712328767123288, 0],
|
||||
[0, 0, 0, 0]]), (0, 1))
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
The default value of ``normalize_last=True`` can provide significant
|
||||
speedup to row reduction, especially on matrices with symbols. However,
|
||||
if you depend on the form row reduction algorithm leaves entries
|
||||
of the matrix, set ``normalize_last=False``
|
||||
"""
|
||||
# Try to use DomainMatrix for ZZ or QQ
|
||||
dM = _to_DM_ZZ_QQ(M)
|
||||
|
||||
if dM is not None:
|
||||
# Use DomainMatrix for ZZ or QQ
|
||||
mat, pivot_cols = _rref_dm(dM)
|
||||
else:
|
||||
# Use the generic Matrix routine.
|
||||
if isinstance(simplify, FunctionType):
|
||||
simpfunc = simplify
|
||||
else:
|
||||
simpfunc = _simplify
|
||||
|
||||
mat, pivot_cols, _ = _row_reduce(M, iszerofunc, simpfunc,
|
||||
normalize_last, normalize=True, zero_above=True)
|
||||
|
||||
if pivots:
|
||||
return mat, pivot_cols
|
||||
else:
|
||||
return mat
|
||||
Reference in New Issue
Block a user