changeset 27084:c0d8ce61c1c9 stable

Always reserve at least 1 element of storage for sparse matrices (bug #56232). * Sparse.h (SparseRep (void), SparseRep (octave_idx_type n), SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz = 1)): Rewrite constructors to always create valid d and r pointers with enough memory for 1 value. Always initialize nzmx to at least 1. * Sparse.cc (SparseRep::change_length): Check for nz == 0 and change value to 1 so that sparse array always maintains memory for at least one value. * amd.cc (Famd): Re-write failing BIST test that now works. * data.cc (Fnzmax): Document that nzmax will always return at least 1. * sparse.cc (Fspalloc): Document that 1 element will always be allocated.
author Rik <rik@octave.org>
date Wed, 08 May 2019 15:18:32 -0700
parents 227feb6e422a
children 9a5b6b929f75 47b81207cb32
files libinterp/corefcn/data.cc libinterp/corefcn/sparse.cc libinterp/dldfcn/amd.cc liboctave/array/Sparse.cc liboctave/array/Sparse.h
diffstat 5 files changed, 28 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/data.cc	Mon May 06 20:25:15 2019 -0700
+++ b/libinterp/corefcn/data.cc	Wed May 08 15:18:32 2019 -0700
@@ -2813,9 +2813,12 @@
 @deftypefn {} {@var{n} =} nzmax (@var{SM})
 Return the amount of storage allocated to the sparse matrix @var{SM}.
 
-Note that Octave tends to crop unused memory at the first opportunity
+Programming Note: Octave tends to crop unused memory at the first opportunity
 for sparse objects.  Thus, in general the value of @code{nzmax} will be the
-same as @code{nnz} except for some cases of user-created sparse objects.
+same as @code{nnz}, except for some cases of user-created sparse objects.
+
+Also, note that Octave always reserves storage for at least one value.  Thus,
+for empty matrices @code{nnz} will report 0, but @code{nzmax} will report 1.
 @seealso{nnz, spalloc, sparse}
 @end deftypefn */)
 {
--- a/libinterp/corefcn/sparse.cc	Mon May 06 20:25:15 2019 -0700
+++ b/libinterp/corefcn/sparse.cc	Wed May 08 15:18:32 2019 -0700
@@ -267,9 +267,9 @@
 @b{and} that the following conditions are met:
 
 @itemize
-@item the assignment does not decrease nnz (@var{S}).
+@item the assignment does not decrease @code{nnz (@var{S})}.
 
-@item after the assignment, nnz (@var{S}) does not exceed @var{nz}.
+@item after the assignment, @code{nnz (@var{S})} does not exceed @var{nz}.
 
 @item no index is out of bounds.
 @end itemize
@@ -281,6 +281,9 @@
 
 The amount of pre-allocated memory for a given matrix may be queried using
 the function @code{nzmax}.
+
+Programming Note: Octave always reserves memory for at least one value,
+even if @var{nz} is 0.
 @seealso{nzmax, sparse}
 @end deftypefn */)
 {
--- a/libinterp/dldfcn/amd.cc	Mon May 06 20:25:15 2019 -0700
+++ b/libinterp/dldfcn/amd.cc	Wed May 08 15:18:32 2019 -0700
@@ -182,7 +182,7 @@
 %!shared A, A2, opts
 %! A = ones (20, 30);
 %! A2 = ones (30, 30);
-%!
+
 %!testif HAVE_AMD
 %! assert(amd (A2), [1:30]);
 %! opts.dense = 25;
@@ -190,7 +190,9 @@
 %! opts.aggressive = 1;
 %! assert(amd (A2, opts), [1:30]);
 
+%!testif HAVE_AMD
+%! assert (amd ([]), zeros (1,0))
+
 %!error <S must be a square matrix|was unavailable or disabled> amd (A)
 %!error amd (A2, 2)
-%!error <matrix S is corrupted|was unavailable or disabled> amd ([])
 */
--- a/liboctave/array/Sparse.cc	Mon May 06 20:25:15 2019 -0700
+++ b/liboctave/array/Sparse.cc	Wed May 08 15:18:32 2019 -0700
@@ -143,6 +143,9 @@
   for (octave_idx_type j = ncols; j > 0 && c[j] > nz; j--)
     c[j] = nz;
 
+  // Always preserve space for 1 element.
+  nz = (nz > 0 ? nz : 1);
+
   // Skip reallocation if we have less than 1/frac extra elements to discard.
   static const int frac = 5;
   if (nz > nzmx || nz < nzmx - nzmx/frac)
--- a/liboctave/array/Sparse.h	Mon May 06 20:25:15 2019 -0700
+++ b/liboctave/array/Sparse.h	Wed May 08 15:18:32 2019 -0700
@@ -68,20 +68,22 @@
     octave::refcount<int> count;
 
     SparseRep (void)
-      : d (nullptr), r (nullptr), c (new octave_idx_type [1] {}),
-        nzmx (0), nrows (0), ncols (0), count (1)
+      : d (new T [1]), r (new octave_idx_type [1] {}),
+        c (new octave_idx_type [1] {}),
+        nzmx (1), nrows (0), ncols (0), count (1)
     { }
 
     SparseRep (octave_idx_type n)
-      : d (nullptr), r (nullptr), c (new octave_idx_type [n+1] {}),
-        nzmx (0), nrows (n), ncols (n), count (1)
+      : d (new T [1]), r (new octave_idx_type [1] {}),
+        c (new octave_idx_type [n+1] {}),
+        nzmx (1), nrows (n), ncols (n), count (1)
     { }
 
-    SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz = 0)
-      : d (nz > 0 ? new T [nz] : nullptr),
-        r (nz > 0 ? new octave_idx_type [nz] {} : nullptr),
-        c (new octave_idx_type [nc+1] {}), nzmx (nz), nrows (nr),
-        ncols (nc), count (1)
+    SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz = 1)
+      : d (nz > 0 ? new T [nz] : new T [1]),
+        r (nz > 0 ? new octave_idx_type [nz] {} : new octave_idx_type [1] {}),
+        c (new octave_idx_type [nc+1] {}),
+        nzmx (nz > 0 ? nz : 1), nrows (nr), ncols (nc), count (1)
     { }
 
     SparseRep (const SparseRep& a)