GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
find.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 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 "quit.h"
31 
32 #include "defun.h"
33 #include "error.h"
34 #include "errwarn.h"
35 #include "ovl.h"
36 
37 // Find at most N_TO_FIND nonzero elements in NDA. Search forward if
38 // DIRECTION is 1, backward if it is -1. NARGOUT is the number of
39 // output arguments. If N_TO_FIND is -1, find all nonzero elements.
40 
41 template <typename T>
43 find_nonzero_elem_idx (const Array<T>& nda, int nargout,
44  octave_idx_type n_to_find, int direction)
45 {
46  octave_value_list retval ((nargout == 0 ? 1 : nargout), Matrix ());
47 
49  if (n_to_find >= 0)
50  idx = nda.find (n_to_find, direction == -1);
51  else
52  idx = nda.find ();
53 
54  // The maximum element is always at the end.
55  octave_idx_type iext = (idx.isempty () ? 0 : idx.xelem (idx.numel () - 1) + 1);
56 
57  switch (nargout)
58  {
59  default:
60  case 3:
61  retval(2) = Array<T> (nda.index (idx_vector (idx)));
62  OCTAVE_FALLTHROUGH;
63 
64  case 2:
65  {
66  Array<octave_idx_type> jdx (idx.dims ());
67  octave_idx_type n = idx.numel ();
68  octave_idx_type nr = nda.rows ();
69  for (octave_idx_type i = 0; i < n; i++)
70  {
71  jdx.xelem (i) = idx.xelem (i) / nr;
72  idx.xelem (i) %= nr;
73  }
74  iext = -1;
75  retval(1) = idx_vector (jdx, -1);
76  }
77  OCTAVE_FALLTHROUGH;
78 
79  case 1:
80  case 0:
81  retval(0) = idx_vector (idx, iext);
82  break;
83  }
84 
85  return retval;
86 }
87 
88 template <typename T>
90 find_nonzero_elem_idx (const Sparse<T>& v, int nargout,
91  octave_idx_type n_to_find, int direction)
92 {
93  nargout = std::min (nargout, 5);
94  octave_value_list retval ((nargout == 0 ? 1 : nargout), Matrix ());
95 
96  octave_idx_type nr = v.rows ();
97  octave_idx_type nc = v.cols ();
98  octave_idx_type nz = v.nnz ();
99 
100  // Search in the default range.
101  octave_idx_type start_nc = -1;
102  octave_idx_type end_nc = -1;
103  octave_idx_type count;
104 
105  // Search for the range to search
106  if (n_to_find < 0)
107  {
108  start_nc = 0;
109  end_nc = nc;
110  n_to_find = nz;
111  }
112  else if (direction > 0)
113  {
114  for (octave_idx_type j = 0; j < nc; j++)
115  {
116  octave_quit ();
117 
118  if (v.cidx (j) == 0 && v.cidx (j+1) != 0)
119  start_nc = j;
120  if (v.cidx (j+1) >= n_to_find)
121  {
122  end_nc = j + 1;
123  break;
124  }
125  }
126  }
127  else
128  {
129  for (octave_idx_type j = nc; j > 0; j--)
130  {
131  octave_quit ();
132 
133  if (v.cidx (j) == nz && v.cidx (j-1) != nz)
134  end_nc = j;
135  if (nz - v.cidx (j-1) >= n_to_find)
136  {
137  start_nc = j - 1;
138  break;
139  }
140  }
141  }
142 
143  count = (n_to_find > v.cidx (end_nc) - v.cidx (start_nc) ?
144  v.cidx (end_nc) - v.cidx (start_nc) : n_to_find);
145 
146  octave_idx_type result_nr;
147  octave_idx_type result_nc;
148 
149  // Default case is to return a column vector, however, if the original
150  // argument was a row vector, then force return of a row vector.
151  if (nr == 1)
152  {
153  result_nr = 1;
154  result_nc = count;
155  }
156  else
157  {
158  result_nr = count;
159  result_nc = 1;
160  }
161 
162  Matrix idx (result_nr, result_nc);
163 
164  Matrix i_idx (result_nr, result_nc);
165  Matrix j_idx (result_nr, result_nc);
166 
167  Array<T> val (dim_vector (result_nr, result_nc));
168 
169  if (count > 0)
170  {
171  // Search for elements to return. Only search the region where there
172  // are elements to be found using the count that we want to find.
173  for (octave_idx_type j = start_nc, cx = 0; j < end_nc; j++)
174  for (octave_idx_type i = v.cidx (j); i < v.cidx (j+1); i++)
175  {
176  octave_quit ();
177 
178  if (direction < 0 && i < nz - count)
179  continue;
180  i_idx(cx) = static_cast<double> (v.ridx (i) + 1);
181  j_idx(cx) = static_cast<double> (j + 1);
182  idx(cx) = j * nr + v.ridx (i) + 1;
183  val(cx) = v.data(i);
184  cx++;
185  if (cx == count)
186  break;
187  }
188  }
189  else
190  {
191  // No items found. Fixup return dimensions for Matlab compatibility.
192  // The behavior to match is documented in Array.cc (Array<T>::find).
193  if ((nr == 0 && nc == 0) || (nr == 1 && nc == 1))
194  {
195  idx.resize (0, 0);
196 
197  i_idx.resize (0, 0);
198  j_idx.resize (0, 0);
199 
200  val.resize (dim_vector (0, 0));
201  }
202  }
203 
204  switch (nargout)
205  {
206  case 0:
207  case 1:
208  retval(0) = idx;
209  break;
210 
211  case 5:
212  retval(4) = nc;
213  OCTAVE_FALLTHROUGH;
214 
215  case 4:
216  retval(3) = nr;
217  OCTAVE_FALLTHROUGH;
218 
219  case 3:
220  retval(2) = val;
221  OCTAVE_FALLTHROUGH;
222 
223  case 2:
224  retval(1) = j_idx;
225  retval(0) = i_idx;
226  }
227 
228  return retval;
229 }
230 
232 find_nonzero_elem_idx (const PermMatrix& v, int nargout,
233  octave_idx_type n_to_find, int direction)
234 {
235  // There are far fewer special cases to handle for a PermMatrix.
236  nargout = std::min (nargout, 5);
237  octave_value_list retval ((nargout == 0 ? 1 : nargout), Matrix ());
238 
239  octave_idx_type nr = v.rows ();
240  octave_idx_type nc = v.cols ();
241  octave_idx_type start_nc, count;
242 
243  // Determine the range to search.
244  if (n_to_find < 0 || n_to_find >= nc)
245  {
246  start_nc = 0;
247  count = nc;
248  }
249  else if (direction > 0)
250  {
251  start_nc = 0;
252  count = n_to_find;
253  }
254  else
255  {
256  start_nc = nc - n_to_find;
257  count = n_to_find;
258  }
259 
260  Matrix idx (count, 1);
261  Matrix i_idx (count, 1);
262  Matrix j_idx (count, 1);
263  // Every value is 1.
264  Array<double> val (dim_vector (count, 1), 1.0);
265 
266  if (count > 0)
267  {
268  const Array<octave_idx_type>& p = v.col_perm_vec ();
269  for (octave_idx_type k = 0; k < count; k++)
270  {
271  octave_quit ();
272 
273  const octave_idx_type j = start_nc + k;
274  const octave_idx_type i = p(j);
275  i_idx(k) = static_cast<double> (1+i);
276  j_idx(k) = static_cast<double> (1+j);
277  idx(k) = j * nc + i + 1;
278  }
279  }
280  else
281  {
282  // FIXME: Is this case even possible? A scalar permutation matrix seems
283  // to devolve to a scalar full matrix, at least from the Octave command
284  // line. Perhaps this function could be called internally from C++ with
285  // such a matrix.
286  // No items found. Fixup return dimensions for Matlab compatibility.
287  // The behavior to match is documented in Array.cc (Array<T>::find).
288  if ((nr == 0 && nc == 0) || (nr == 1 && nc == 1))
289  {
290  idx.resize (0, 0);
291 
292  i_idx.resize (0, 0);
293  j_idx.resize (0, 0);
294 
295  val.resize (dim_vector (0, 0));
296  }
297  }
298 
299  switch (nargout)
300  {
301  case 0:
302  case 1:
303  retval(0) = idx;
304  break;
305 
306  case 5:
307  retval(4) = nc;
308  OCTAVE_FALLTHROUGH;
309 
310  case 4:
311  retval(3) = nc;
312  OCTAVE_FALLTHROUGH;
313 
314  case 3:
315  retval(2) = val;
316  OCTAVE_FALLTHROUGH;
317 
318  case 2:
319  retval(1) = j_idx;
320  retval(0) = i_idx;
321  }
322 
323  return retval;
324 }
325 
326 DEFUN (find, args, nargout,
327  doc: /* -*- texinfo -*-
328 @deftypefn {} {@var{idx} =} find (@var{x})
329 @deftypefnx {} {@var{idx} =} find (@var{x}, @var{n})
330 @deftypefnx {} {@var{idx} =} find (@var{x}, @var{n}, @var{direction})
331 @deftypefnx {} {[i, j] =} find (@dots{})
332 @deftypefnx {} {[i, j, v] =} find (@dots{})
333 Return a vector of indices of nonzero elements of a matrix, as a row if
334 @var{x} is a row vector or as a column otherwise.
335 
336 To obtain a single index for each matrix element, Octave pretends that the
337 columns of a matrix form one long vector (like Fortran arrays are stored).
338 For example:
339 
340 @example
341 @group
342 find (eye (2))
343  @result{} [ 1; 4 ]
344 @end group
345 @end example
346 
347 If two inputs are given, @var{n} indicates the maximum number of elements to
348 find from the beginning of the matrix or vector.
349 
350 If three inputs are given, @var{direction} should be one of
351 @qcode{"first"} or @qcode{"last"}, requesting only the first or last
352 @var{n} indices, respectively. However, the indices are always returned in
353 ascending order.
354 
355 If two outputs are requested, @code{find} returns the row and column
356 indices of nonzero elements of a matrix. For example:
357 
358 @example
359 @group
360 [i, j] = find (2 * eye (2))
361  @result{} i = [ 1; 2 ]
362  @result{} j = [ 1; 2 ]
363 @end group
364 @end example
365 
366 If three outputs are requested, @code{find} also returns a vector
367 containing the nonzero values. For example:
368 
369 @example
370 @group
371 [i, j, v] = find (3 * eye (2))
372  @result{} i = [ 1; 2 ]
373  @result{} j = [ 1; 2 ]
374  @result{} v = [ 3; 3 ]
375 @end group
376 @end example
377 
378 Note that this function is particularly useful for sparse matrices, as
379 it extracts the nonzero elements as vectors, which can then be used to
380 create the original matrix. For example:
381 
382 @example
383 @group
384 sz = size (a);
385 [i, j, v] = find (a);
386 b = sparse (i, j, v, sz(1), sz(2));
387 @end group
388 @end example
389 @seealso{nonzeros}
390 @end deftypefn */)
391 {
392  int nargin = args.length ();
393 
394  if (nargin < 1 || nargin > 3)
395  print_usage ();
396 
397  // Setup the default options.
398  octave_idx_type n_to_find = -1;
399  if (nargin > 1)
400  {
401  double val = args(1).xscalar_value ("find: N must be an integer");
402 
403  if (val < 0 || (! octave::math::isinf (val)
404  && val != octave::math::fix (val)))
405  error ("find: N must be a non-negative integer");
406  else if (! octave::math::isinf (val))
407  n_to_find = val;
408  }
409 
410  // Direction to do the searching (1 == forward, -1 == reverse).
411  int direction = 1;
412  if (nargin > 2)
413  {
414  std::string s_arg = args(2).string_value ();
415 
416  if (s_arg == "first")
417  direction = 1;
418  else if (s_arg == "last")
419  direction = -1;
420  else
421  error (R"(find: DIRECTION must be "first" or "last")");
422  }
423 
425 
426  octave_value arg = args(0);
427 
428  if (arg.islogical ())
429  {
430  if (arg.issparse ())
431  {
433 
434  retval = find_nonzero_elem_idx (v, nargout, n_to_find, direction);
435  }
436  else if (nargout <= 1 && n_to_find == -1 && direction == 1)
437  {
438  // This case is equivalent to extracting indices from a logical
439  // matrix. Try to reuse the possibly cached index vector.
440 
441  // No need to catch index_exception, since arg is bool.
442  // Out-of-range errors have already set pos, and will be
443  // caught later.
444 
445  octave_value result = arg.index_vector ().unmask ();
446 
447  dim_vector dv = result.dims ();
448 
449  retval(0) = (dv.all_zero () || dv.isvector ()
450  ? result : result.reshape (dv.as_column ()));
451  }
452  else
453  {
454  boolNDArray v = arg.bool_array_value ();
455 
456  retval = find_nonzero_elem_idx (v, nargout, n_to_find, direction);
457  }
458  }
459  else if (arg.isinteger ())
460  {
461 #define DO_INT_BRANCH(INTT) \
462  else if (arg.is_ ## INTT ## _type ()) \
463  { \
464  INTT ## NDArray v = arg.INTT ## _array_value (); \
465  \
466  retval = find_nonzero_elem_idx (v, nargout, n_to_find, direction); \
467  }
468 
469  if (false)
470  ;
471  DO_INT_BRANCH (int8)
472  DO_INT_BRANCH (int16)
473  DO_INT_BRANCH (int32)
474  DO_INT_BRANCH (int64)
475  DO_INT_BRANCH (uint8)
476  DO_INT_BRANCH (uint16)
477  DO_INT_BRANCH (uint32)
478  DO_INT_BRANCH (uint64)
479  else
480  panic_impossible ();
481  }
482  else if (arg.issparse ())
483  {
484  if (arg.isreal ())
485  {
487 
488  retval = find_nonzero_elem_idx (v, nargout, n_to_find, direction);
489  }
490  else if (arg.iscomplex ())
491  {
493 
494  retval = find_nonzero_elem_idx (v, nargout, n_to_find, direction);
495  }
496  else
497  err_wrong_type_arg ("find", arg);
498  }
499  else if (arg.is_perm_matrix ())
500  {
501  PermMatrix P = arg.perm_matrix_value ();
502 
503  retval = find_nonzero_elem_idx (P, nargout, n_to_find, direction);
504  }
505  else if (arg.is_string ())
506  {
507  charNDArray chnda = arg.char_array_value ();
508 
509  retval = find_nonzero_elem_idx (chnda, nargout, n_to_find, direction);
510  }
511  else if (arg.is_single_type ())
512  {
513  if (arg.isreal ())
514  {
515  FloatNDArray nda = arg.float_array_value ();
516 
517  retval = find_nonzero_elem_idx (nda, nargout, n_to_find, direction);
518  }
519  else if (arg.iscomplex ())
520  {
522 
523  retval = find_nonzero_elem_idx (cnda, nargout, n_to_find, direction);
524  }
525  }
526  else if (arg.isreal ())
527  {
528  NDArray nda = arg.array_value ();
529 
530  retval = find_nonzero_elem_idx (nda, nargout, n_to_find, direction);
531  }
532  else if (arg.iscomplex ())
533  {
534  ComplexNDArray cnda = arg.complex_array_value ();
535 
536  retval = find_nonzero_elem_idx (cnda, nargout, n_to_find, direction);
537  }
538  else
539  err_wrong_type_arg ("find", arg);
540 
541  return retval;
542 }
543 
544 /*
545 %!assert (find (char ([0, 97])), 2)
546 %!assert (find ([1, 0, 1, 0, 1]), [1, 3, 5])
547 %!assert (find ([1; 0; 3; 0; 1]), [1; 3; 5])
548 %!assert (find ([0, 0, 2; 0, 3, 0; -1, 0, 0]), [3; 5; 7])
549 
550 %!assert <*53603> (find (ones (1,1,2) > 0), [1;2])
551 %!assert <*53603> (find (ones (1,1,1,3) > 0), [1;2;3])
552 
553 %!test
554 %! [i, j, v] = find ([0, 0, 2; 0, 3, 0; -1, 0, 0]);
555 %!
556 %! assert (i, [3; 2; 1]);
557 %! assert (j, [1; 2; 3]);
558 %! assert (v, [-1; 3; 2]);
559 
560 %!assert (find (single ([1, 0, 1, 0, 1])), [1, 3, 5])
561 %!assert (find (single ([1; 0; 3; 0; 1])), [1; 3; 5])
562 %!assert (find (single ([0, 0, 2; 0, 3, 0; -1, 0, 0])), [3; 5; 7])
563 
564 %!test
565 %! [i, j, v] = find (single ([0, 0, 2; 0, 3, 0; -1, 0, 0]));
566 %!
567 %! assert (i, [3; 2; 1]);
568 %! assert (j, [1; 2; 3]);
569 %! assert (v, single ([-1; 3; 2]));
570 
571 %!test
572 %! pcol = [5 1 4 3 2];
573 %! P = eye (5) (:, pcol);
574 %! [i, j, v] = find (P);
575 %! [ifull, jfull, vfull] = find (full (P));
576 %! assert (i, ifull);
577 %! assert (j, jfull);
578 %! assert (all (v == 1));
579 
580 %!test
581 %! prow = [5 1 4 3 2];
582 %! P = eye (5) (prow, :);
583 %! [i, j, v] = find (P);
584 %! [ifull, jfull, vfull] = find (full (P));
585 %! assert (i, ifull);
586 %! assert (j, jfull);
587 %! assert (all (v == 1));
588 
589 %!assert <*53655> (find (false), zeros (0, 0))
590 %!assert <*53655> (find ([false, false]), zeros (1, 0))
591 %!assert <*53655> (find ([false; false]), zeros (0, 1))
592 %!assert <*53655> (find ([false, false; false, false]), zeros (0, 1))
593 
594 %!assert (find ([2 0 1 0 5 0], 1), 1)
595 %!assert (find ([2 0 1 0 5 0], 2, "last"), [3, 5])
596 
597 %!assert (find ([2 0 1 0 5 0], Inf), [1, 3, 5])
598 %!assert (find ([2 0 1 0 5 0], Inf, "last"), [1, 3, 5])
599 
600 %!error find ()
601 */
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:128
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
Definition: Array.cc:2225
octave_idx_type rows(void) const
Definition: Array.h:415
Array< T > index(const idx_vector &i) const
Indexing without resizing.
Definition: Array.cc:698
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:453
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
Definition: dMatrix.h:42
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
const Array< octave_idx_type > & col_perm_vec(void) const
Definition: PermMatrix.h:81
octave_idx_type cols(void) const
Definition: PermMatrix.h:61
octave_idx_type rows(void) const
Definition: PermMatrix.h:60
Definition: Sparse.h:49
octave_idx_type cols(void) const
Definition: Sparse.h:251
T * data(void)
Definition: Sparse.h:470
octave_idx_type * cidx(void)
Definition: Sparse.h:492
octave_idx_type nnz(void) const
Actual number of nonzero terms.
Definition: Sparse.h:238
octave_idx_type rows(void) const
Definition: Sparse.h:250
octave_idx_type * ridx(void)
Definition: Sparse.h:479
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
bool isvector(void) const
Definition: dim-vector.h:461
dim_vector as_column(void) const
Definition: dim-vector.h:445
bool all_zero(void) const
Definition: dim-vector.h:366
idx_vector unmask(void) const
Definition: idx-vector.cc:1205
boolNDArray bool_array_value(bool warn=false) const
Definition: ov.h:844
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:853
bool isreal(void) const
Definition: ov.h:691
bool issparse(void) const
Definition: ov.h:706
bool is_string(void) const
Definition: ov.h:593
ComplexNDArray complex_array_value(bool frc_str_conv=false) const
Definition: ov.h:831
charNDArray char_array_value(bool frc_str_conv=false) const
Definition: ov.h:850
bool isinteger(void) const
Definition: ov.h:683
octave_value reshape(const dim_vector &dv) const
Definition: ov.h:530
PermMatrix perm_matrix_value(void) const
Definition: ov.h:876
SparseBoolMatrix sparse_bool_matrix_value(bool warn=false) const
Definition: ov.h:860
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:812
bool is_single_type(void) const
Definition: ov.h:651
idx_vector index_vector(bool require_integers=false) const
Definition: ov.h:493
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition: ov.h:835
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition: ov.h:815
bool is_perm_matrix(void) const
Definition: ov.h:590
bool iscomplex(void) const
Definition: ov.h:694
bool islogical(void) const
Definition: ov.h:688
dim_vector dims(void) const
Definition: ov.h:500
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:857
static octave_idx_type find(octave_idx_type i, octave_idx_type *pp)
Definition: colamd.cc:104
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error(const char *fmt,...)
Definition: error.cc:968
#define panic_impossible()
Definition: error.h:380
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
octave_value_list find_nonzero_elem_idx(const Array< T > &nda, int nargout, octave_idx_type n_to_find, int direction)
Definition: find.cc:43
#define DO_INT_BRANCH(INTT)
octave_idx_type n
Definition: mx-inlines.cc:753
double fix(double x)
Definition: lo-mappers.h:118
bool isinf(double x)
Definition: lo-mappers.h:203
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811