Hi,

> Indeed. Could you please prepare a patch? We'll merge it.

Right. That's why we should allow the user to pass his own simplifying
> functions. I think there will always be cases when simplify cannot do
> it, so that when the user can pass it's own zero finder, he can fix
> that easily. And let's report all failing cases as bugs to simplify.
> We'll then judge on case by case basis if simplify should handle it or
> not and how it should be approached.
>


Here is a patch with an optional zero finder function argument in the inv
method plus a note about the pivoting problem in the docstring. What do you
think?

And I will add issues for the cases where simplify fails to detect a zero
that I found so far.

Regards,

Henrik

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/sympy?hl=en
-~----------~----~----~----~------~----~------~--~---

diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py
index b686f7b..3742e35 100644
--- a/sympy/matrices/matrices.py
+++ b/sympy/matrices/matrices.py
@@ -46,6 +46,9 @@ def _dims_to_nm( dims ):
 
     return n, m
 
+def _iszero(x):
+    return x == 0
+
 class Matrix(object):
 
     # Added just for numpy compatibility
@@ -469,7 +472,7 @@ class Matrix(object):
     def __repr__(self):
         return StrPrinter.doprint(self)
 
-    def inv(self, method="GE"):
+    def inv(self, method="GE", iszerofunc=_iszero):
         """
         Calculates the matrix inverse.
 
@@ -479,12 +482,18 @@ class Matrix(object):
           LU .... inverse_LU()
           ADJ ... inverse_ADJ()
 
+        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.
+
         """
         assert self.cols==self.lines
         if method == "GE":
-            return self.inverse_GE()
+            return self.inverse_GE(iszerofunc=iszerofunc)
         elif method == "LU":
-            return self.inverse_LU()
+            return self.inverse_LU(iszerofunc=iszerofunc)
         elif method == "ADJ":
             return self.inverse_ADJ()
         else:
@@ -756,13 +765,13 @@ class Matrix(object):
             s+="]\n"
         print s
 
-    def LUsolve(self, rhs):
+    def LUsolve(self, rhs, iszerofunc=_iszero):
         """
         Solve the linear system Ax = b.
         self is the coefficient matrix A and rhs is the right side b.
         """
         assert rhs.lines == self.lines
-        A, perm = self.LUdecomposition_Simple()
+        A, perm = self.LUdecomposition_Simple(iszerofunc=_iszero)
         n = self.lines
         b = rhs.permuteFwd(perm)
         # forward substitution, all diag entries are scaled to 1
@@ -776,11 +785,11 @@ class Matrix(object):
             b.row(i, lambda x,k: x / A[i,i])
         return b
 
-    def LUdecomposition(self):
+    def LUdecomposition(self, iszerofunc=_iszero):
         """
         Returns the decompositon LU and the row swaps p.
         """
-        combined, p = self.LUdecomposition_Simple()
+        combined, p = self.LUdecomposition_Simple(iszerofunc=_iszero)
         L = self.zeros(self.lines)
         U = self.zeros(self.lines)
         for i in range(self.lines):
@@ -793,7 +802,7 @@ class Matrix(object):
                     U[i,j] = combined[i,j]
         return L, U, p
 
-    def LUdecomposition_Simple(self):
+    def LUdecomposition_Simple(self, iszerofunc=_iszero):
         """
         Returns A compused of L,U (L's diag entries are 1) and
         p which is the list of the row swaps (in order).
@@ -812,14 +821,14 @@ class Matrix(object):
                 for k in range(j):
                     A[i,j] = A[i,j] - A[i,k]*A[k,j]
                 # find the first non-zero pivot, includes any expression
-                if pivot == -1 and A[i,j] != 0:
+                if pivot == -1 and not iszerofunc(A[i,j]):
                     pivot = i
             if pivot < 0:
                 raise "Error: non-invertible matrix passed to LUdecomposition_Simple()"
             if pivot != j: # row must be swapped
                 A.row_swap(pivot,j)
                 p.append([pivot,j])
-            assert A[j,j] != 0
+            assert not iszerofunc(A[j,j])
             scale = 1 / A[j,j]
             for i in range(j+1,n):
                 A[i,j] = A[i,j] * scale
@@ -1145,20 +1154,20 @@ class Matrix(object):
         return self.cofactorMatrix(method).T
 
 
-    def inverse_LU(self):
+    def inverse_LU(self, iszerofunc=_iszero):
         """
         Calculates the inverse using LU decomposition.
         """
-        return self.LUsolve(self.eye(self.lines))
+        return self.LUsolve(self.eye(self.lines), iszerofunc=_iszero)
 
-    def inverse_GE(self):
+    def inverse_GE(self, iszerofunc=_iszero):
         """
         Calculates the inverse using Gaussian elimination.
         """
         assert self.lines == self.cols
         assert self.det() != 0
         big = self.row_join(self.eye(self.lines))
-        red = big.rref()
+        red = big.rref(iszerofunc=iszerofunc)
         return red[0][:,big.lines:]
 
     def inverse_ADJ(self):
@@ -1170,7 +1179,7 @@ class Matrix(object):
         assert d != 0
         return self.adjugate()/d
 
-    def rref(self,simplified=False):
+    def rref(self,simplified=False, iszerofunc=_iszero):
         """
         Take any matrix and return reduced row-echelon form and indices of pivot vars
 
@@ -1184,13 +1193,13 @@ class Matrix(object):
                 break
             if simplified:
                 r[pivots,i] = simplify(r[pivots,i])
-            if r[pivots,i] == 0:
+            if iszerofunc(r[pivots,i]):
                 for k in range(pivots, r.lines):
                     if simplified and k>pivots:
                         r[k,i] = simplify(r[k,i])
-                    if r[k,i] != 0:
+                    if not iszerofunc(r[k,i]):
                         break
-                if k == r.lines - 1 and r[k,i] == 0:
+                if k == r.lines - 1 and iszerofunc(r[k,i]):
                     continue
                 r.row_swap(pivots,k)
             scale = r[pivots,i]
diff --git a/sympy/matrices/tests/test_matrices.py b/sympy/matrices/tests/test_matrices.py
index 2e47544..15682ba 100644
--- a/sympy/matrices/tests/test_matrices.py
+++ b/sympy/matrices/tests/test_matrices.py
@@ -938,3 +938,9 @@ def test_issue650():
     assert Matrix([[(exp(x)-1)/x, 2*x + y*x, x**x ],
                     [1/x, abs(x) , abs(sin(x+1))]]).limit(x, 0) == Matrix([[1, 0, 1],[oo, 0, sin(1)]])
     assert a.integrate(x) == Matrix([[Rational(1,3)*x**3, y*x**2/2],[x**2*sin(y)/2, x**2*cos(y)/2]])
+
+def test_inv_iszerofunc():
+    A = eye(4)
+    A.col_swap(0,1)
+    for method in "GE", "LU":
+        assert A.inv(method, iszerofunc=lambda x: x==0) == A.inv("ADJ")

Reply via email to