GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
sparse.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cstdlib>
31 #include <string>
32 
33 #include "variables.h"
34 #include "utils.h"
35 #include "pager.h"
36 #include "defun.h"
37 #include "errwarn.h"
38 #include "quit.h"
39 #include "unwind-prot.h"
40 
41 #include "ov-re-sparse.h"
42 #include "ov-cx-sparse.h"
43 #include "ov-bool-sparse.h"
44 
46 
47 DEFUN (issparse, args, ,
48  doc: /* -*- texinfo -*-
49 @deftypefn {} {@var{tf} =} issparse (@var{x})
50 Return true if @var{x} is a sparse matrix.
51 @seealso{ismatrix}
52 @end deftypefn */)
53 {
54  if (args.length () != 1)
55  print_usage ();
56 
57  return ovl (args(0).issparse ());
58 }
59 
60 /*
61 %!assert (issparse (sparse (1)), true)
62 %!assert (issparse (1), false)
63 %!assert (issparse (sparse (false)), true)
64 %!assert (issparse (true), false)
65 %!test
66 %! warning ("off", "Octave:sparse:double-conversion", "local");
67 %! assert (issparse (sparse (single ([1 2]))), true);
68 %!assert (issparse (single ([1, 2])), false)
69 %!assert (issparse (sparse ([1+i, 2]')), true)
70 %!assert (issparse ([1+i, 2]'), false)
71 
72 %!assert (issparse ([]), false)
73 %!assert (issparse (sparse([])), true)
74 %!assert (issparse ("test"), false)
75 %!assert (issparse (struct ("one", {1})), false)
76 %!assert (issparse (cell (1)), false)
77 
78 ## Test input validation
79 %!error issparse ()
80 %!error issparse (1,2)
81 */
82 
83 DEFUN (sparse, args, ,
84  doc: /* -*- texinfo -*-
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})
88 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n})
89 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}, "unique")
90 @deftypefnx {} {@var{S} =} sparse (@var{i}, @var{j}, @var{sv}, @var{m}, @var{n}, @var{nzmax})
91 Create a sparse matrix from a full matrix @var{A} or row, column, value
92 triplets.
93 
94 If @var{A} is a full matrix, convert it to a sparse matrix representation,
95 removing all zero values in the process. The matrix @var{A} should be of type
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.
100 
101 Given the integer index vectors @var{i} and @var{j}, and a 1-by-@code{nnz}
102 vector of real or complex values @var{sv}, construct the sparse matrix
103 @code{S(@var{i}(@var{k}),@var{j}(@var{k})) = @var{sv}(@var{k})} with overall
104 dimensions @var{m} and @var{n}. If any of @var{i}, @var{j}, or @var{sv} are
105 scalars, they are expanded to have a common size.
106 
107 If @var{m} or @var{n} are not specified then their values are derived from the
108 maximum index in the vectors @var{i} and @var{j} as given by
109 @w{@code{@var{m} = max (@var{i})}}, @w{@code{@var{n} = max (@var{j})}}.
110 
111 @strong{Note}: If multiple values are specified with the same @var{i},
112 @var{j} indices, the corresponding value in @var{S} will be the sum of the
113 values at the repeated location. @xref{XREFaccumarray,,@code{accumarray}}, for
114 an example of how to produce different behavior such as taking the minimum
115 instead.
116 
117 If the option @qcode{"unique"} is given, and more than one value is specified
118 at the same @var{i}, @var{j} indices, then only the last specified value will
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.
121 
122 @code{sparse (@var{m}, @var{n})} will create an empty @var{m}x@var{n} sparse
123 matrix and is equivalent to @code{sparse ([], [], [], @var{m}, @var{n})}
124 
125 The optional final argument reserves space for @var{nzmax} values in the sparse
126 array and is useful if the eventual number of nonzero values will be greater
127 than the number of values in @var{sv} used during the initial construction of
128 the array. @xref{XREFspalloc,,@code{spalloc}}, for more information and usage
129 instructions.
130 
131 Example 1 (convert full matrix to sparse to save memory):
132 
133 @example
134 @group
135 x = full (diag (1:1000));
136 sizeof (x)
137 @result{} 8000000
138 s = sparse (x);
139 sizeof (xs)
140 @result{} 24008
141 @end group
142 @end example
143 
144 Example 2 (sum at repeated indices):
145 
146 @example
147 @group
148 @var{i} = [1 1 2]; @var{j} = [1 1 2]; @var{sv} = [3 4 5];
149 sparse (@var{i}, @var{j}, @var{sv}, 3, 4)
150 @result{}
151  Compressed Column Sparse (rows = 3, cols = 4, nnz = 2 [17%])
152 
153  (1, 1) -> 7
154  (2, 2) -> 5
155 @end group
156 @end example
157 
158 Example 3 ("unique" option):
159 
160 @example
161 @group
162 @var{i} = [1 1 2]; @var{j} = [1 1 2]; @var{sv} = [3 4 5];
163 sparse (@var{i}, @var{j}, @var{sv}, 3, 4, "unique")
164 @result{}
165  Compressed Column Sparse (rows = 3, cols = 4, nnz = 2 [17%])
166 
167  (1, 1) -> 4
168  (2, 2) -> 5
169 @end group
170 @end example
171 @seealso{full, accumarray, spalloc, spdiags, speye, spones, sprand, sprandn,
172 sprandsym, spconvert, spfun}
173 @end deftypefn */)
174 {
175  int nargin = args.length ();
176 
177  if (nargin == 0 || nargin > 6)
178  print_usage ();
179 
180  octave_value retval;
181 
182  if (nargin == 1)
183  {
184  octave_value arg = args(0);
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 ())
196  retval = arg.sparse_bool_matrix_value ();
197  else
198  err_wrong_type_arg ("sparse", arg);
199  }
200  else if (nargin == 2)
201  {
202  octave_idx_type m = args(0).xidx_type_value ("sparse: M must be a non-negative integer");
203  octave_idx_type n = args(1).xidx_type_value ("sparse: N must be a non-negative integer");
204 
205  if (m < 0 || n < 0)
206  error ("sparse: dimensions M and N must be non-negative");
207 
208  retval = SparseMatrix (m, n);
209  }
210  else if (nargin >= 3)
211  {
212  bool summation = true;
213  if (nargin > 3 && args(nargin-1).is_string ())
214  {
215  std::string opt = args(nargin-1).string_value ();
216  if (opt == "unique")
217  summation = false;
218  else if (opt == "sum" || opt == "summation")
219  summation = true;
220  else
221  error ("sparse: invalid option: %s", opt.c_str ());
222 
223  nargin -= 1;
224  }
225 
226  octave_idx_type m, n, nzmax;
227  m = n = nzmax = -1;
228  if (nargin == 6)
229  {
230  nzmax = args(5).idx_type_value ();
231  nargin--;
232  }
233 
234  if (nargin == 5)
235  {
236  m = args(3).xidx_type_value ("sparse: M must be a non-negative integer");
237  n = args(4).xidx_type_value ("sparse: N must be a non-negative integer");
238 
239  if (m < 0 || n < 0)
240  error ("sparse: dimensions M and N must be non-negative");
241  }
242 
243  int argidx = 0; // index being checked when index_vector throws
244  try
245  {
246  idx_vector i = args(0).index_vector ();
247  argidx = 1;
248  idx_vector j = args(1).index_vector ();
249 
250  octave_value arg = args(2); // temp var for code readability
251  if (arg.isfloat ())
252  {
253  if (arg.is_single_type ())
254  warning_with_id ("Octave:sparse:double-conversion",
255  "sparse: input array cast to double");
256  if (arg.iscomplex ())
257  retval = SparseComplexMatrix (arg.complex_array_value (),
258  i, j, m, n, summation, nzmax);
259  else
260  retval = SparseMatrix (arg.array_value (),
261  i, j, m, n, summation, nzmax);
262  }
263  else if (arg.islogical ())
264  retval = SparseBoolMatrix (arg.bool_array_value (),
265  i, j, m, n, summation, nzmax);
266  else
267  err_wrong_type_arg ("sparse", arg);
268  }
269  catch (index_exception& ie)
270  {
271  // Rethrow to allow more info to be reported later.
272  ie.set_pos_if_unset (2, argidx+1);
273  throw;
274  }
275  }
276 
277  return retval;
278 }
279 
280 /*
281 ## Tests for sparse constructor are in test/sparse.tst
282 %!assert (1)
283 */
284 
285 DEFUN (spalloc, args, ,
286  doc: /* -*- texinfo -*-
287 @deftypefn {} {@var{s} =} spalloc (@var{m}, @var{n}, @var{nz})
288 Create an @var{m}-by-@var{n} sparse matrix with pre-allocated space for at
289 most @var{nz} nonzero elements.
290 
291 This is useful for building a matrix incrementally by a sequence of indexed
292 assignments. Subsequent indexed assignments after @code{spalloc} will reuse
293 the pre-allocated memory, provided they are of one of the simple forms
294 
295 @itemize
296 @item @code{@var{s}(I:J) = @var{x}}
297 
298 @item @code{@var{s}(:,I:J) = @var{x}}
299 
300 @item @code{@var{s}(K:L,I:J) = @var{x}}
301 @end itemize
302 
303 @b{and} that the following conditions are met:
304 
305 @itemize
306 @item the assignment does not decrease @code{nnz (@var{S})}.
307 
308 @item after the assignment, @code{nnz (@var{S})} does not exceed @var{nz}.
309 
310 @item no index is out of bounds.
311 @end itemize
312 
313 Partial movement of data may still occur, but in general the assignment will
314 be more memory and time efficient under these circumstances. In particular,
315 it is possible to efficiently build a pre-allocated sparse matrix from a
316 contiguous block of columns.
317 
318 The amount of pre-allocated memory for a given matrix may be queried using
319 the function @code{nzmax}.
320 
321 Programming Note: Octave always reserves memory for at least one value,
322 even if @var{nz} is 0.
323 @seealso{nzmax, sparse}
324 @end deftypefn */)
325 {
326  int nargin = args.length ();
327 
328  if (nargin < 2 || nargin > 3)
329  print_usage ();
330 
331  octave_idx_type m = args(0).idx_type_value ();
332  octave_idx_type n = args(1).idx_type_value ();
333 
334  octave_idx_type nz = 0;
335  if (nargin == 3)
336  nz = args(2).idx_type_value ();
337 
338  if (m < 0 || n < 0 || nz < 0)
339  error ("spalloc: M, N, and NZ must be non-negative");
340 
341  return ovl (SparseMatrix (dim_vector (m, n), nz));
342 }
343 
344 /*
345 %!assert (issparse (spalloc (1,1)))
346 %!assert (spalloc (1,1), sparse (1,1))
347 %!test
348 %! s = spalloc (1,1,5);
349 %! assert (s, sparse (1,1));
350 %! assert (nzmax (s), 5);
351 %!assert (spalloc (1,2), sparse (1,2))
352 %!assert (spalloc (1,2,2), sparse (1,2))
353 %!assert (spalloc (2,1), sparse (2,1))
354 %!assert (spalloc (2,1,2), sparse (2,1))
355 
356 %!error spalloc ()
357 %!error spalloc (1)
358 %!error spalloc (1,2,3,4)
359 %!error <M, N, and NZ must be non-negative> spalloc (-1, 1, 1)
360 %!error <M, N, and NZ must be non-negative> spalloc (1, -1, 1)
361 %!error <M, N, and NZ must be non-negative> spalloc (1, 1, -1)
362 */
363 
364 OCTAVE_END_NAMESPACE(octave)
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
bool isfloat() const
Definition: ov.h:701
boolNDArray bool_array_value(bool warn=false) const
Definition: ov.h:891
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:900
ComplexNDArray complex_array_value(bool frc_str_conv=false) const
Definition: ov.h:878
bool is_single_type() const
Definition: ov.h:698
bool iscomplex() const
Definition: ov.h:741
SparseBoolMatrix sparse_bool_matrix_value(bool warn=false) const
Definition: ov.h:907
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:859
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:904
bool islogical() const
Definition: ov.h:735
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1078
void() error(const char *fmt,...)
Definition: error.cc:988
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219