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