GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
perms.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2023-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 <algorithm>
31 
32 #include "defun.h"
33 #include "error.h"
34 #include "errwarn.h"
35 #include "ovl.h"
36 
38 
39 static inline double
40 Factorial (octave_idx_type n)
41 {
42  double ret = 1;
43  for (octave_idx_type i = 2; i <= n; i++)
44  ret *= i;
45  return ret;
46 }
47 
48 //
49 // Use C++ template to cater for the different octave array classes.
50 //
51 template <typename T>
52 static inline Array<T>
53 GetPerms (const Array<T>& ar_in, bool uniq_v, bool do_sort = false)
54 {
55  octave_idx_type m = ar_in.numel ();
56  double nr = Factorial (m);
57 
58  // Setup index vector filled from 0..m-1
59  OCTAVE_LOCAL_BUFFER (int, myvidx, m);
60  for (int i = 0; i < m; i++)
61  myvidx[i] = i;
62 
63  // Interim array to sort ar_in for octave sort order and to implement
64  // "unique".
65  Array<T> ar (ar_in);
66 
67  if (uniq_v)
68  {
69  ar = ar.sort (ar.dims () (1) > ar.dims () (0) ? 1 : 0, ASCENDING);
70  const T *Ar = ar.data ();
71  int ctr = 0;
72  int N_el = 1;
73 
74  // Number of same elements where we need to remove permutations
75  // Number of unique permutations is n! / (n_el1! * n_el2! * ...)
76  for (octave_idx_type i = 0; i < m - 1; i++)
77  {
78  myvidx[i] = ctr;
79  if (Ar[i + 1] != Ar[i])
80  {
81  nr /= Factorial (N_el);
82  ctr = i + 1; // index of next different element
83  N_el = 1;
84  }
85  else
86  N_el++;
87  }
88  myvidx[m - 1] = ctr;
89  nr /= Factorial (N_el);
90  }
91  else if (do_sort)
92  {
93  ar = ar.sort (ar.dims () (1) > ar.dims () (0) ? 1 : 0, ASCENDING);
94  }
95 
96  // Sort vector indices for inverse lexicographic order later.
97  std::sort (myvidx, myvidx + m, std::greater<int> ());
98 
99  const T *Ar = ar.data ();
100 
101  // Set up result array
102  octave_idx_type n = static_cast<octave_idx_type> (nr);
103  Array<T> res (dim_vector (n, m));
104  T *Res = res.fortran_vec ();
105 
106  // Do the actual job
107  octave_idx_type i = 0;
108  std::sort (myvidx, myvidx + m, std::greater<int> ());
109  do
110  {
111  for (octave_idx_type j = 0; j < m; j++)
112  Res[i + j * n] = Ar[myvidx[j]];
113  i++;
114  }
115  while (std::next_permutation (myvidx, myvidx + m, std::greater<int> ()));
116 
117  return res;
118 }
119 
120 // Template for non-numerical types (e.g. Cell) without sorting.
121 // The C++ compiler complains as the provided type octave_value does not
122 // support the test of equality via '==' in the above template.
123 
124 template <typename T>
125 static inline Array<T>
126 GetPermsNoSort (const Array<T>& ar_in, bool uniq_v = false)
127 {
128  octave_idx_type m = ar_in.numel ();
129  double nr = Factorial (m);
130 
131  // Setup index vector filled from 0..m-1
132  OCTAVE_LOCAL_BUFFER (int, myvidx, m);
133  for (int i = 0; i < m; i++)
134  myvidx[i] = i;
135 
136  const T *Ar = ar_in.data ();
137 
138  if (uniq_v)
139  {
140  // Mutual Comparison using is_equal to detect duplicated values
141  int N_el = 1;
142  // Number of unique permutations is n! / (n_el1! * n_el2! * ...)
143  for (octave_idx_type i = 0; i < m - 1; i++)
144  {
145  for (octave_idx_type j = i + 1; j < m; j++)
146  {
147  if (myvidx[j] > myvidx[i] && Ar[i].is_equal (Ar[j]))
148  {
149  myvidx[j] = myvidx[i]; // not yet processed...
150  N_el++;
151  }
152  else
153  {
154  nr /= Factorial (N_el);
155  N_el = 1;
156  }
157  }
158  }
159  nr /= Factorial (N_el);
160  }
161 
162  // Sort vector indices for inverse lexicographic order later.
163  std::sort (myvidx, myvidx + m, std::greater<int> ());
164 
165  // Set up result array
166  octave_idx_type n = static_cast<octave_idx_type> (nr);
167  Array<T> res (dim_vector (n, m));
168  T *Res = res.fortran_vec ();
169 
170  // Do the actual job
171  octave_idx_type i = 0;
172  do
173  {
174  for (octave_idx_type j = 0; j < m; j++)
175  Res[i + j * n] = Ar[myvidx[j]];
176  i++;
177  }
178  while (std::next_permutation (myvidx, myvidx + m, std::greater<int> ()));
179 
180  return res;
181 }
182 
183 DEFUN (perms, args, ,
184  doc: /* -*- texinfo -*-
185 @deftypefn {} {@var{P} =} perms (@var{v})
186 @deftypefnx {} {@var{P} =} perms (@var{v}, "unique")
187 Generate all permutations of vector @var{v} with one row per permutation.
188 
189 Results are returned in reverse lexicographic order if @var{v} is in ascending
190 order. If @var{v} is in a different permutation, then the result is permuted
191 that way too. Consequently, an input in descending order yields a result in
192 normal lexicographic order. The result has size
193 @code{factorial (@var{n}) * @var{n}}, where @var{n} is the length of @var{v}.
194 Any repeated elements are included in the output.
195 
196 If the optional argument @qcode{"unique"} is given then only unique
197 permutations are returned, using less memory and taking less time than calling
198 @code{unique (perms (@var{v}), "rows")}.
199 
200 Example 1
201 
202 @example
203 @group
204 perms ([1, 2, 3])
205 @result{}
206 3 2 1
207 3 1 2
208 2 3 1
209 2 1 3
210 1 3 2
211 1 2 3
212 @end group
213 @end example
214 
215 Example 2
216 
217 @example
218 @group
219 perms ([1, 1, 2, 2], "unique")
220 @result{}
221 2 2 1 1
222 2 1 2 1
223 2 1 1 2
224 1 2 2 1
225 1 2 1 2
226 1 1 2 2
227 @end group
228 @end example
229 
230 Programming Note: If the @qcode{"unique"} option is not used, the length of
231 @var{v} should be no more than 10-12 to limit memory consumption. Even with
232 @qcode{"unique"}, there should be no more than 10-12 unique elements in
233 @var{v}.
234 @seealso{permute, randperm, nchoosek}
235 
236 @end deftypefn */)
237 {
238  int nargin = args.length ();
239 
240  if (nargin < 1 || nargin > 2)
241  print_usage ();
242 
243  octave_value retval;
244 
245  // Parameter check "unique"
246  bool uniq_v = false;
247  if (nargin == 2)
248  {
249  const charMatrix opt = args (1).char_matrix_value ();
250  const char *str = opt.data ();
251  if (std::string (str, opt.cols ()) != "unique")
252  {
253  error ("perms: option must be the string \"unique\".");
254  }
255  uniq_v = true;
256  }
257 
258  if (! (args (0).is_matrix_type () || args (0).is_range ()
259  || args (0).iscell () || args (0).is_scalar_type ()
260  || args (0).isstruct ()))
261  {
262  error ("perms: INPUT must be a matrix, a range, a cell array, "
263  "a struct or a scalar.");
264  }
265 
266  std::string clname = args (0).class_name ();
267 
268  // Execute main permutation code for the different classes
269  if (clname == "double")
270  retval = GetPerms<double> (args (0).array_value (), uniq_v);
271  else if (clname == "single")
272  retval = GetPerms<float> (args (0).float_array_value (), uniq_v);
273  else if (clname == "logical")
274  retval = GetPerms<bool> (args (0).bool_array_value (), uniq_v);
275  else if (clname == "char")
276  retval = GetPerms<char> (args (0).char_array_value (), uniq_v);
277  else if (clname == "int8")
278  retval = GetPerms<octave_int8> (args (0).int8_array_value (), uniq_v);
279  else if (clname == "int16")
280  retval = GetPerms<octave_int16> (args (0).int16_array_value (), uniq_v);
281  else if (clname == "int32")
282  retval = GetPerms<octave_int32> (args (0).int32_array_value (), uniq_v);
283  else if (clname == "int64")
284  retval = GetPerms<octave_int64> (args (0).int64_array_value (), uniq_v);
285  else if (clname == "uint8")
286  retval = GetPerms<octave_uint8> (args (0).uint8_array_value (), uniq_v);
287  else if (clname == "uint16")
288  retval = GetPerms<octave_uint16> (args (0).uint16_array_value (), uniq_v);
289  else if (clname == "uint32")
290  retval = GetPerms<octave_uint32> (args (0).uint32_array_value (), uniq_v);
291  else if (clname == "uint64")
292  retval = GetPerms<octave_uint64> (args (0).uint64_array_value (), uniq_v);
293  else if (clname == "cell")
294  retval = GetPermsNoSort<octave_value> (args (0).cell_value (), uniq_v);
295  else if (clname == "struct")
296  {
297  const octave_map map_in (args (0).map_value ());
298  string_vector fn = map_in.fieldnames ();
299  if (fn.numel () == 0 && map_in.numel () != 0)
300  {
301  octave_scalar_map out;
302  retval = out;
303  }
304  else
305  {
306  octave_map out;
307  if (fn.numel () == 0)
308  {
309  out = octave_map (dim_vector (1, 0));
310  }
311  else
312  {
313  for (octave_idx_type i = 0; i < fn.numel (); i++)
314  {
315  out.assign (fn (i), GetPermsNoSort<octave_value>
316  (map_in.contents (fn (i)), uniq_v));
317  }
318  }
319  retval = out;
320  }
321  }
322  else // none of the above class criteria were met
323  {
324  warning ("perms: unable to permute for class %s", clname.c_str ());
325  // retval stays empty
326  }
327  return ovl (retval);
328 }
329 
330 /*
331 
332 %!assert (rows (perms (1:6)), factorial (6))
333 %!assert (perms (pi), pi)
334 %!assert (perms ([e, pi]), [pi, e; e, pi])
335 %!assert (perms ([pi, e]), [e, pi; pi, e])
336 %!assert (perms ([1 2 3]), [3 2 1; 3 1 2; 2 3 1; 2 1 3; 1 3 2; 1 2 3])
337 %!assert (sortrows (perms (1:5)), sortrows (perms ([2 5 4 1 3]')))
338 %!assert (perms ("abc"), char ("cba", "cab", "bca", "bac", "acb", "abc"))
339 %!assert (sortrows (perms ("fobar")), unique (perms ("fobar"), "rows"))
340 %!assert (unique (perms (1:5)(:))', 1:5)
341 %!assert (perms (int8 (1:4)), int8 (perms (1:4)))
342 
343 %!assert (sortrows (perms ("abb", "unique")), ["abb"; "bab"; "bba"])
344 %!assert (size (perms ([1 1 1 1 2 2 2 3 3], "unique")), [1260 9])
345 %!assert (size (perms (int8([1 1 1 1 1 2 2 2 2 3 3 3]), "unique")), [27720 12])
346 
347 ## Should work for any array type, such as cells and structs,
348 ## and not only for numeric data.
349 
350 %!assert <*52431> (perms ({1}), {1})
351 %!assert <*52431> (perms ({0.1, "foo"}), {"foo", 0.1; 0.1, "foo"})
352 %!assert <*52431> (perms ({"foo", 0.1}), {0.1, "foo"; "foo", 0.1})
353 %!assert <*52431> (perms ({"foo"; 0.1}), {0.1, "foo"; "foo", 0.1})
354 %!assert <*52431> (perms ({0.1; "foo"}), {"foo", 0.1; 0.1, "foo"})
355 %!assert <*52431> (perms ({"foo", "bar"}), {"bar", "foo"; "foo", "bar"})
356 %!assert <*52431> (perms ({"bar", "foo"}), {"foo", "bar"; "bar", "foo"})
357 %!
358 %!assert <*52431> (perms (struct ()), struct ())
359 %!assert <*52431> (perms (struct ("foo", {1, 2})),
360 %! struct ("foo", {2, 1; 1, 2}))
361 %!assert <*52431> (perms (struct ("foo", {1, 2}, "bar", {3, 4})),
362 %! struct ("foo", {2, 1; 1, 2}, "bar", {4, 3; 3, 4}))
363 
364 ## Also sort logical input with order dependent on the input order and
365 ## not their values.
366 
367 %!assert <*52431> (perms (logical ([1 0])), logical ([0 1;, 1 0]))
368 %!assert <*52431> (perms (logical ([0 1])), logical ([1 0; 0 1]))
369 %!assert <*52431> (perms (logical ([0 1 0])),
370 %! logical ([0 1 0; 0 0 1; 1 0 0; 1 0 0; 0 0 1; 0 1 0]))
371 %!assert <*52431> (perms (logical ([0 1 1])),
372 %! logical ([1 1 0; 1 0 1; 1 1 0; 1 0 1; 0 1 1; 0 1 1]))
373 
374 %!assert <*52432> (perms ([]), reshape ([], 1, 0))
375 %!assert <*52432> (perms (single ([])), reshape (single ([]), 1, 0))
376 %!assert <*52432> (perms (int8 ([])), reshape (int8 ([]), 1, 0))
377 %!assert <*52432> (perms ({}), cell (1, 0))
378 
379 %!test <*52432>
380 %! s = struct ();
381 %! s(1) = [];
382 %! assert (perms (reshape (s, 0, 0)), reshape (s, 1, 0));
383 %! assert (perms (reshape (s, 0, 1)), reshape (s, 1, 0));
384 
385 ## test if "unique" works also for cell arrays
386 %!assert <*63965> (perms ({"foo"; "foo"}, "unique"), {"foo", "foo"})
387 %!assert <*63965> (perms ({"foo", "foo", "bar"}, "unique"), ...
388 %! {"bar", "foo", "foo";
389 %! "foo", "bar", "foo";
390 %! "foo", "foo", "bar"})
391 
392 ## Test input validation
393 %!error <Invalid call> perms ()
394 %!error <option must be the string "unique"> perms (1:5, "foobar")
395 %!error <option must be the string "unique"> perms (1:5, {"foo"})
396 
397 */
398 
399 OCTAVE_END_NAMESPACE (octave)
const T * data() const
Size of the specified dimension.
Definition: Array.h:663
octave_idx_type cols() const
Definition: Array.h:469
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
string_vector fieldnames() const
Definition: oct-map.h:332
octave_idx_type numel() const
Definition: oct-map.h:368
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:344
const Cell & contents(const_iterator p) const
Definition: oct-map.h:310
octave_idx_type numel() const
Definition: str-vec.h:100
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(const char *fmt,...)
Definition: error.cc:1063
void() error(const char *fmt,...)
Definition: error.cc:988
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
@ ASCENDING
Definition: oct-sort.h:97
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219