comparison libinterp/corefcn/sparse.cc @ 32058:e242124f1240

Overhaul input validation of sparse() function. * sparse.cc (Fsparse): Decode input type and identify floating point inputs. If input is of single type, emit new warning "Octave:sparse:double-conversion". If input is neither floating point or logical, then call err_wrong_type_arg() for pretty error message. Rename temporary variable 'k' to "argidx" for clarity. New temporary variable "arg" to increase readability of code. Add FIXME note about unreachable code due to behavior of get_dimensions(). * sparse.cc (Fissparse): Turn off warning about double-conversion temporarily for test which has single input. * warning_ids.m: Add description for new warning ID "Octave:sparse:double-conversion". * mk-sparse-tst.sh: Redo BIST tests for sparse() construction.
author Rik <rik@octave.org>
date Wed, 26 Apr 2023 10:09:09 -0700
parents 87beb2f167ea
children bade9602c5a1
comparison
equal deleted inserted replaced
32057:f010a32986e4 32058:e242124f1240
60 /* 60 /*
61 %!assert (issparse (sparse (1)), true) 61 %!assert (issparse (sparse (1)), true)
62 %!assert (issparse (1), false) 62 %!assert (issparse (1), false)
63 %!assert (issparse (sparse (false)), true) 63 %!assert (issparse (sparse (false)), true)
64 %!assert (issparse (true), false) 64 %!assert (issparse (true), false)
65 %!assert (issparse (sparse (single ([1 2]))), true) 65 %!test
66 %! warning ("off", "Octave:sparse:double-conversion", "local");
67 %! assert (issparse (sparse (single ([1 2]))), true);
66 %!assert (issparse (single ([1, 2])), false) 68 %!assert (issparse (single ([1, 2])), false)
67 %!assert (issparse (sparse ([1+i, 2]')), true) 69 %!assert (issparse (sparse ([1+i, 2]')), true)
68 %!assert (issparse ([1+i, 2]'), false) 70 %!assert (issparse ([1+i, 2]'), false)
69 71
70 %!assert (issparse ([]), false) 72 %!assert (issparse ([]), false)
79 */ 81 */
80 82
81 DEFUN (sparse, args, , 83 DEFUN (sparse, args, ,
82 doc: /* -*- texinfo -*- 84 doc: /* -*- texinfo -*-
83 @deftypefn {} {@var{S} =} sparse (@var{A}) 85 @deftypefn {} {@var{S} =} sparse (@var{A})
86 @deftypefnx {} {@var{S} =} sparse (@var{m}, @var{n})
87 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv})
84 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}) 88 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n})
85 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}) 89 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}, "unique")
86 @deftypefnx {} {@var{S} =} sparse (@var{m}, @var{n})
87 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{s}, @var{m}, @var{n}, "unique")
88 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}, @var{nzmax}) 90 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}, @var{nzmax})
89 Create a sparse matrix from a full matrix @var{A} or row, column, value 91 Create a sparse matrix from a full matrix @var{A} or row, column, value
90 triplets. 92 triplets.
91 93
92 If @var{A} is a full matrix, convert it to a sparse matrix representation, 94 If @var{A} is a full matrix, convert it to a sparse matrix representation,
93 removing all zero values in the process. The matrix @var{A} should be of type 95 removing all zero values in the process. The matrix @var{A} should be of type
94 logical or double. 96 logical or double.
97
98 If two inputs @var{m} (rows) and @var{n} (columns) are specified then create
99 an empty sparse matrix with the specified dimensions.
95 100
96 Given the integer index vectors @var{i} and @var{j}, and a 1-by-@code{nnz} 101 Given the integer index vectors @var{i} and @var{j}, and a 1-by-@code{nnz}
97 vector of real or complex values @var{sv}, construct the sparse matrix 102 vector of real or complex values @var{sv}, construct the sparse matrix
98 @code{S(@var{i}(@var{k}),@var{j}(@var{k})) = @var{sv}(@var{k})} with overall 103 @code{S(@var{i}(@var{k}),@var{j}(@var{k})) = @var{sv}(@var{k})} with overall
99 dimensions @var{m} and @var{n}. If any of @var{i}, @var{j}, or @var{sv} are 104 dimensions @var{m} and @var{n}. If any of @var{i}, @var{j}, or @var{sv} are
109 an example of how to produce different behavior such as taking the minimum 114 an example of how to produce different behavior such as taking the minimum
110 instead. 115 instead.
111 116
112 If the option @qcode{"unique"} is given, and more than one value is specified 117 If the option @qcode{"unique"} is given, and more than one value is specified
113 at the same @var{i}, @var{j} indices, then only the last specified value will 118 at the same @var{i}, @var{j} indices, then only the last specified value will
114 be used. 119 be used. For completeness, the option @qcode{"sum"} can be given and will
120 be ignored as the default behavior is to sum values at repeated locations.
115 121
116 @code{sparse (@var{m}, @var{n})} will create an empty @var{m}x@var{n} sparse 122 @code{sparse (@var{m}, @var{n})} will create an empty @var{m}x@var{n} sparse
117 matrix and is equivalent to @code{sparse ([], [], [], @var{m}, @var{n})} 123 matrix and is equivalent to @code{sparse ([], [], [], @var{m}, @var{n})}
118 124
119 The optional final argument reserves space for @var{nzmax} values in the sparse 125 The optional final argument reserves space for @var{nzmax} values in the sparse
174 octave_value retval; 180 octave_value retval;
175 181
176 if (nargin == 1) 182 if (nargin == 1)
177 { 183 {
178 octave_value arg = args(0); 184 octave_value arg = args(0);
179 if (arg.islogical ()) 185 if (arg.isfloat ())
186 {
187 if (arg.is_single_type ())
188 warning_with_id ("Octave:sparse:double-conversion",
189 "sparse: input array cast to double");
190 if (arg.iscomplex ())
191 retval = arg.sparse_complex_matrix_value ();
192 else
193 retval = arg.sparse_matrix_value ();
194 }
195 else if (arg.islogical ())
180 retval = arg.sparse_bool_matrix_value (); 196 retval = arg.sparse_bool_matrix_value ();
181 else if (arg.iscomplex ())
182 retval = arg.sparse_complex_matrix_value ();
183 else if (arg.isnumeric ())
184 retval = arg.sparse_matrix_value ();
185 else 197 else
186 err_wrong_type_arg ("sparse", arg); 198 err_wrong_type_arg ("sparse", arg);
187 } 199 }
188 else if (nargin == 2) 200 else if (nargin == 2)
189 { 201 {
190 octave_idx_type m = 0; 202 octave_idx_type m = 0;
191 octave_idx_type n = 0; 203 octave_idx_type n = 0;
192 204
193 get_dimensions (args(0), args(1), "sparse", m, n); 205 get_dimensions (args(0), args(1), "sparse", m, n);
194 206
207 // FIXME: this code is never active because get_dimensions()
208 // replaces negative dimensions with 0.
195 if (m < 0 || n < 0) 209 if (m < 0 || n < 0)
196 error ("sparse: dimensions must be non-negative"); 210 error ("sparse: dimensions must be non-negative");
197 211
198 retval = SparseMatrix (m, n); 212 retval = SparseMatrix (m, n);
199 } 213 }
223 237
224 if (nargin == 5) 238 if (nargin == 5)
225 { 239 {
226 get_dimensions (args(3), args(4), "sparse", m, n); 240 get_dimensions (args(3), args(4), "sparse", m, n);
227 241
242 // FIXME: this code is never active because get_dimensions()
243 // replaces negative dimensions with 0.
228 if (m < 0 || n < 0) 244 if (m < 0 || n < 0)
229 error ("sparse: dimensions must be non-negative"); 245 error ("sparse: dimensions must be non-negative");
230 } 246 }
231 247
232 int k = 0; // index we're checking when index_vector throws 248 int argidx = 0; // index we're checking when index_vector throws
233 try 249 try
234 { 250 {
235 idx_vector i = args(0).index_vector (); 251 idx_vector i = args(0).index_vector ();
236 k = 1; 252 argidx = 1;
237 idx_vector j = args(1).index_vector (); 253 idx_vector j = args(1).index_vector ();
238 254
239 if (args(2).islogical ()) 255 octave_value arg = args(2); // temp var for code readability
240 retval = SparseBoolMatrix (args(2).bool_array_value (), i, j, 256 if (arg.isfloat ())
241 m, n, summation, nzmax); 257 {
242 else if (args(2).iscomplex ()) 258 if (arg.is_single_type ())
243 retval = SparseComplexMatrix (args(2).complex_array_value(), 259 warning_with_id ("Octave:sparse:double-conversion",
244 i, j, m, n, summation, nzmax); 260 "sparse: input array cast to double");
245 else if (args(2).isnumeric ()) 261 if (arg.iscomplex ())
246 retval = SparseMatrix (args(2).array_value (), i, j, 262 retval = SparseComplexMatrix (arg.complex_array_value (),
247 m, n, summation, nzmax); 263 i, j, m, n, summation, nzmax);
264 else
265 retval = SparseMatrix (arg.array_value (),
266 i, j, m, n, summation, nzmax);
267 }
268 else if (arg.islogical ())
269 retval = SparseBoolMatrix (arg.bool_array_value (),
270 i, j, m, n, summation, nzmax);
248 else 271 else
249 err_wrong_type_arg ("sparse", args(2)); 272 err_wrong_type_arg ("sparse", arg);
250 } 273 }
251 catch (index_exception& ie) 274 catch (index_exception& ie)
252 { 275 {
253 // Rethrow to allow more info to be reported later. 276 // Rethrow to allow more info to be reported later.
254 ie.set_pos_if_unset (2, k+1); 277 ie.set_pos_if_unset (2, argidx+1);
255 throw; 278 throw;
256 } 279 }
257 } 280 }
258 281
259 return retval; 282 return retval;