changeset 27093:6e18f0ce268c

Inverse of a sparse/diagonal singular matrix should be a sparse/diagonal matrix of Infs. * liboctave/array/CSparse.cc(inverse): if the matrix is null, return an error. If it is singular, return a sparse matrix of Infs. * liboctave/array/dSparse.cc(inverse): if the matrix is null, return an error. If it is singular, return a sparse matrix of Infs. * liboctave/array/CDiagMatrix.cc(inverse): if the matrix is null, return an error. If it is singular, return a diagonal matrix of Infs. * liboctave/array/dDiagMatrix.cc(inverse): if the matrix is null, return an error. If it is singular, return a diagonal matrix of Infs. * libinterp/corefcn/inv.cc: set rcond to zero for singular diagonal matrices. Add tests for the above cases. * test/diag-perm.tst: modify a test for the inversion of a singular diagonal matrix.
author marco.caliari@univr.it
date Tue, 14 May 2019 17:00:52 +0200
parents d1fe5bd5e005
children f16471efcdf4
files libinterp/corefcn/inv.cc liboctave/array/CDiagMatrix.cc liboctave/array/CSparse.cc liboctave/array/dDiagMatrix.cc liboctave/array/dSparse.cc test/diag-perm.tst
diffstat 6 files changed, 124 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/inv.cc	Sat May 18 13:18:00 2019 +0200
+++ b/libinterp/corefcn/inv.cc	Tue May 14 17:00:52 2019 +0200
@@ -83,13 +83,17 @@
           if (isfloat)
             {
               result = arg.float_complex_diag_matrix_value ().inverse (info);
-              if (nargout > 1)
+              if (info == -1)
+                frcond = 0.0f;
+              else if (nargout > 1)
                 frcond = arg.float_complex_diag_matrix_value ().rcond ();
             }
           else
             {
               result = arg.complex_diag_matrix_value ().inverse (info);
-              if (nargout > 1)
+              if (info == -1)
+                rcond = 0.0;
+              else if (nargout > 1)
                 rcond = arg.complex_diag_matrix_value ().rcond ();
             }
         }
@@ -98,13 +102,17 @@
           if (isfloat)
             {
               result = arg.float_diag_matrix_value ().inverse (info);
-              if (nargout > 1)
+              if (info == -1)
+                frcond = 0.0f;
+              else if (nargout > 1)
                 frcond = arg.float_diag_matrix_value ().rcond ();
             }
           else
             {
               result = arg.diag_matrix_value ().inverse (info);
-              if (nargout > 1)
+              if (info == -1)
+                rcond = 0.0;
+              else if (nargout > 1)
                 rcond = arg.diag_matrix_value ().rcond ();
             }
         }
@@ -189,7 +197,7 @@
   if (isfloat)
     {
       volatile float xrcond = frcond;
-      rcond_plus_one_eq_one = xrcond + 1.0F == 1.0F;
+      rcond_plus_one_eq_one = xrcond + 1.0f == 1.0f;
     }
   else
     {
@@ -225,10 +233,29 @@
 %! assert (isa (xinv, "double"));
 %! assert (isa (rcond, "double"));
 
+%!test
+%! fail ("A = inv (sparse ([1, 2;0 ,0]))", "warning", "matrix singular");
+%! assert (A, sparse ([Inf, Inf; 0, 0]));
+
+%!test
+%! fail ("A = inv (sparse ([1i, 2;0 ,0]))", "warning", "matrix singular");
+%! assert (A, sparse ([Inf, Inf; 0, 0]));
+
+%!test
+%! fail ("A = inv (diag ([1, 0, 1]))", "warning", "matrix singular");
+%! assert (A, diag ([Inf, Inf, Inf]));
+
+%!error <inverse of the null matrix not defined> inv (diag ([0, 0]))
+%!error <inverse of the null matrix not defined> inv (diag (complex ([0, 0])))
+
+%!test
+%! fail ("A = inv (sparse ([1, 0, 0; 0, 0, 0; 0, 0, 1]))", "warning", "matrix singular");
+%! assert (A, sparse ([Inf, 0, 0; 0, 0, 0; 0, 0, Inf]));
+
 %!error inv ()
 %!error inv ([1, 2; 3, 4], 2)
 %!error <must be a square matrix> inv ([1, 2; 3, 4; 5, 6])
-
+%!error <inverse of the null matrix not defined> inv (sparse (2, 2, 0))
 */
 
 DEFALIAS (inverse, inv);
--- a/liboctave/array/CDiagMatrix.cc	Sat May 18 13:18:00 2019 +0200
+++ b/liboctave/array/CDiagMatrix.cc	Tue May 14 17:00:52 2019 +0200
@@ -316,15 +316,35 @@
   ComplexDiagMatrix retval (r, c);
 
   info = 0;
-  for (octave_idx_type i = 0; i < length (); i++)
+  octave_idx_type len = r;        // alias for readability
+  octave_idx_type z_count  = 0;   // zeros
+  octave_idx_type nz_count = 0;   // non-zeros
+  for (octave_idx_type i = 0; i < len; i++)
     {
-      if (elem (i, i) == 0.0)
+      if (xelem (i, i) == 0.0)
         {
-          info = -1;
-          return *this;
+          z_count++;
+          if (nz_count > 0)
+            break;
         }
       else
-        retval.elem (i, i) = 1.0 / elem (i, i);
+        {
+          nz_count++;
+          if (z_count > 0)
+            break;
+          retval.elem (i, i) = 1.0 / xelem (i, i);
+        }
+    }
+  if (nz_count == 0)
+    {
+      (*current_liboctave_error_handler)
+        ("inverse of the null matrix not defined");
+    }
+  else if (z_count > 0)
+    {
+      info = -1;
+      element_type *data = retval.fortran_vec ();
+      std::fill (data, data + len, octave::numeric_limits<double>::Inf ());
     }
 
   return retval;
--- a/liboctave/array/CSparse.cc	Sat May 18 13:18:00 2019 +0200
+++ b/liboctave/array/CSparse.cc	Tue May 14 17:00:52 2019 +0200
@@ -985,6 +985,12 @@
 SparseComplexMatrix::inverse (MatrixType& mattype, octave_idx_type& info,
                               double& rcond, bool, bool calc_cond) const
 {
+  if (nnz () == 0)
+    {
+      (*current_liboctave_error_handler)
+        ("inverse of the null matrix not defined");
+    }
+
   int typ = mattype.type (false);
   SparseComplexMatrix ret;
 
@@ -1035,6 +1041,18 @@
                                                              Qinit, Matrix (),
                                                              false, false);
           rcond = fact.rcond ();
+          if (rcond == 0.0)
+            {
+              // Return all Inf matrix with sparsity pattern of input.
+              octave_idx_type nz = nnz ();
+              ret = SparseComplexMatrix (rows (), cols (), nz);
+              std::fill (ret.xdata (), ret.xdata () + nz,
+                         octave::numeric_limits<double>::Inf ());
+              std::copy_n (ridx (), nz, ret.xridx ());
+              std::copy_n (cidx (), cols () + 1, ret.xcidx ());
+
+              return ret;
+            }
           double rcond2;
           SparseComplexMatrix InvL = fact.L ().transpose ().
                                      tinverse (tmp_typ, info, rcond2,
--- a/liboctave/array/dDiagMatrix.cc	Sat May 18 13:18:00 2019 +0200
+++ b/liboctave/array/dDiagMatrix.cc	Tue May 14 17:00:52 2019 +0200
@@ -233,19 +233,41 @@
 {
   octave_idx_type r = rows ();
   octave_idx_type c = cols ();
-  octave_idx_type len = length ();
   if (r != c)
     (*current_liboctave_error_handler) ("inverse requires square matrix");
 
   DiagMatrix retval (r, c);
 
   info = 0;
+  octave_idx_type len = r;        // alias for readability
+  octave_idx_type z_count  = 0;   // zeros
+  octave_idx_type nz_count = 0;   // non-zeros
   for (octave_idx_type i = 0; i < len; i++)
     {
-      if (elem (i, i) == 0.0)
-        retval.elem (i, i) = octave::numeric_limits<double>::Inf ();
+      if (xelem (i, i) == 0.0)
+        {
+          z_count++;
+          if (nz_count > 0)
+            break;
+        }
       else
-        retval.elem (i, i) = 1.0 / elem (i, i);
+        {
+          nz_count++;
+          if (z_count > 0)
+            break;
+          retval.elem (i, i) = 1.0 / xelem (i, i);
+        }
+    }
+  if (nz_count == 0)
+    {
+      (*current_liboctave_error_handler)
+        ("inverse of the null matrix not defined");
+    }
+  else if (z_count > 0)
+    {
+      info = -1;
+      element_type *data = retval.fortran_vec ();
+      std::fill (data, data + len, octave::numeric_limits<double>::Inf ());
     }
 
   return retval;
--- a/liboctave/array/dSparse.cc	Sat May 18 13:18:00 2019 +0200
+++ b/liboctave/array/dSparse.cc	Tue May 14 17:00:52 2019 +0200
@@ -928,6 +928,12 @@
 SparseMatrix::inverse (MatrixType& mattype, octave_idx_type& info,
                        double& rcond, bool, bool calc_cond) const
 {
+  if (nnz () == 0)
+    {
+      (*current_liboctave_error_handler)
+        ("inverse of the null matrix not defined");
+    }
+
   int typ = mattype.type (false);
   SparseMatrix ret;
 
@@ -977,6 +983,19 @@
                                                       Qinit, Matrix (),
                                                       false, false);
           rcond = fact.rcond ();
+          if (rcond == 0.0)
+            {
+              // Return all Inf matrix with sparsity pattern of input.
+              octave_idx_type nz = nnz ();
+              ret = SparseMatrix (rows (), cols (), nz);
+              std::fill (ret.xdata (), ret.xdata () + nz,
+                         octave::numeric_limits<double>::Inf ());
+              std::copy_n (ridx (), nz, ret.xridx ());
+              std::copy_n (cidx (), cols () + 1, ret.xcidx ());
+
+              return ret;
+            }
+
           double rcond2;
           SparseMatrix InvL = fact.L ().transpose ().tinverse (tmp_typ,
                               info, rcond2, true, false);
--- a/test/diag-perm.tst	Sat May 18 13:18:00 2019 +0200
+++ b/test/diag-perm.tst	Tue May 14 17:00:52 2019 +0200
@@ -265,11 +265,12 @@
 %! assert (full (D - A), D - full (A));
 
 ## inverse preserves diagonal structure even for singular matrices (bug #46103)
+## but set all the diagonal elements to Inf (bug #56232)
 %!test
 %! x = diag (1:3);
 %! assert (inv (x), diag ([1 1/2 1/3]));
-%! x = diag (0:2);
-%! assert (inv (x), diag ([Inf 1 1/2]));
+%!warning <matrix singular> A = inv (diag (0:2));
+%! assert (A, diag ([Inf Inf Inf]));
 
 ## assignment to diagonal elements preserves diagonal structure (bug #36932)
 %!test