GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
find.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2025 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
43template <typename T>
45find_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
91template <typename T>
93find_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
235find_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
329DEFUN (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{})
336Return 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
339To obtain a single index for each matrix element, Octave pretends that the
340columns of a matrix form one long vector (like Fortran arrays are stored).
341For example:
342
343@example
344@group
345find (eye (2))
346 @result{} [ 1; 4 ]
347@end group
348@end example
349
350If two inputs are given, @var{n} indicates the maximum number of elements to
351find from the beginning of the matrix or vector.
352
353If 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
356ascending order.
357
358If two outputs are requested, @code{find} returns the row and column
359indices 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
369If three outputs are requested, @code{find} also returns a vector
370containing 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
381If @var{x} is a multi-dimensional array of size m x n x p x @dots{}, @var{j}
382contains the column locations as if @var{x} was flattened into a
383two-dimensional matrix of size m x (n + p + @dots{}).
384
385Note that this function is particularly useful for sparse matrices, as
386it extracts the nonzero elements as vectors, which can then be used to
387create the original matrix. For example:
388
389@example
390@group
391sz = size (a);
392[i, j, v] = find (a);
393b = 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 const 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 error ("find: unexpected integer type - please report this bug");
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 {
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
619OCTAVE_END_NAMESPACE(octave)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition Array.h:525
Array< T, Alloc > index(const octave::idx_vector &i) const
Indexing without resizing.
octave_idx_type rows() const
Definition Array.h:463
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition dMatrix.h:156
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:349
octave_idx_type * cidx()
Definition Sparse.h:593
T * data()
Definition Sparse.h:571
octave_idx_type * ridx()
Definition Sparse.h:580
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition Sparse.h:336
octave_idx_type rows() const
Definition Sparse.h:348
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
bool all_zero() const
Definition dim-vector.h:296
bool isvector() const
Definition dim-vector.h:391
dim_vector as_column() const
Definition dim-vector.h:375
octave_idx_type length() const
Definition ovl.h:111
bool isinteger() const
Definition ov.h:730
boolNDArray bool_array_value(bool warn=false) const
Definition ov.h:900
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition ov.h:909
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:884
bool is_single_type() const
Definition ov.h:698
charNDArray char_array_value(bool frc_str_conv=false) const
Definition ov.h:906
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:916
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:865
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition ov.h:888
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:868
PermMatrix perm_matrix_value() const
Definition ov.h:932
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition ov.h:913
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()
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:1003
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)