GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
oct-rand.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2003-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 <cassert>
31 #include <cstdint>
32 
33 #include <limits>
34 
35 #include "lo-error.h"
36 #include "lo-ieee.h"
37 #include "lo-mappers.h"
38 #include "lo-ranlib-proto.h"
39 #include "mach-info.h"
40 #include "oct-locbuf.h"
41 #include "oct-rand.h"
42 #include "oct-time.h"
43 #include "quit.h"
44 #include "randgamma.h"
45 #include "randmtzig.h"
46 #include "randpoisson.h"
47 #include "singleton-cleanup.h"
48 
49 namespace octave
50 {
51  rand *rand::instance = nullptr;
52 
53  rand::rand (void)
54  : current_distribution (uniform_dist), use_old_generators (false),
55  rand_states ()
56  {
58 
60  }
61 
62  bool rand::instance_ok (void)
63  {
64  bool retval = true;
65 
66  if (! instance)
67  {
68  instance = new rand ();
70  }
71 
72  return retval;
73  }
74 
75  double rand::do_seed (void)
76  {
77  union d2i { double d; int32_t i[2]; };
78  union d2i u;
79 
81 
82  switch (ff)
83  {
85  F77_FUNC (getsd, GETSD) (u.i[1], u.i[0]);
86  break;
87 
88  default:
89  F77_FUNC (getsd, GETSD) (u.i[0], u.i[1]);
90  break;
91  }
92 
93  return u.d;
94  }
95 
96  static int32_t
97  force_to_fit_range (int32_t i, int32_t lo, int32_t hi)
98  {
99  assert (hi > lo && lo >= 0);
100 
101  i = (i > 0 ? i : -i);
102 
103  if (i < lo)
104  i = lo;
105  else if (i > hi)
106  i = i % hi;
107 
108  return i;
109  }
110 
111  void rand::do_seed (double s)
112  {
113  use_old_generators = true;
114 
115  int i0, i1;
116  union d2i { double d; int32_t i[2]; };
117  union d2i u;
118  u.d = s;
119 
121 
122  switch (ff)
123  {
125  i1 = force_to_fit_range (u.i[0], 1, 2147483563);
126  i0 = force_to_fit_range (u.i[1], 1, 2147483399);
127  break;
128 
129  default:
130  i0 = force_to_fit_range (u.i[0], 1, 2147483563);
131  i1 = force_to_fit_range (u.i[1], 1, 2147483399);
132  break;
133  }
134 
135  F77_FUNC (setsd, SETSD) (i0, i1);
136  }
137 
138  void rand::do_reset (void)
139  {
140  use_old_generators = true;
142  }
143 
144  uint32NDArray rand::do_state (const std::string& d)
145  {
146  return rand_states[d.empty () ? current_distribution : get_dist_id (d)];
147  }
148 
149  void rand::do_state (const uint32NDArray& s, const std::string& d)
150  {
151  use_old_generators = false;
152 
153  int old_dist = current_distribution;
154 
155  int new_dist = (d.empty () ? current_distribution : get_dist_id (d));
156 
157  uint32NDArray saved_state;
158 
159  if (old_dist != new_dist)
160  saved_state = get_internal_state ();
161 
162  set_internal_state (s);
163 
164  rand_states[new_dist] = get_internal_state ();
165 
166  if (old_dist != new_dist)
167  rand_states[old_dist] = saved_state;
168  }
169 
170  void rand::do_reset (const std::string& d)
171  {
172  use_old_generators = false;
173 
174  int old_dist = current_distribution;
175 
176  int new_dist = (d.empty () ? current_distribution : get_dist_id (d));
177 
178  uint32NDArray saved_state;
179 
180  if (old_dist != new_dist)
181  saved_state = get_internal_state ();
182 
184  rand_states[new_dist] = get_internal_state ();
185 
186  if (old_dist != new_dist)
187  rand_states[old_dist] = saved_state;
188  }
189 
190  std::string rand::do_distribution (void)
191  {
192  std::string retval;
193 
194  switch (current_distribution)
195  {
196  case uniform_dist:
197  retval = "uniform";
198  break;
199 
200  case normal_dist:
201  retval = "normal";
202  break;
203 
204  case expon_dist:
205  retval = "exponential";
206  break;
207 
208  case poisson_dist:
209  retval = "poisson";
210  break;
211 
212  case gamma_dist:
213  retval = "gamma";
214  break;
215 
216  default:
217  (*current_liboctave_error_handler)
218  ("rand: invalid distribution ID = %d", current_distribution);
219  break;
220  }
221 
222  return retval;
223  }
224 
225  void rand::do_distribution (const std::string& d)
226  {
227  int id = get_dist_id (d);
228 
229  switch (id)
230  {
231  case uniform_dist:
233  break;
234 
235  case normal_dist:
237  break;
238 
239  case expon_dist:
241  break;
242 
243  case poisson_dist:
245  break;
246 
247  case gamma_dist:
249  break;
250 
251  default:
252  (*current_liboctave_error_handler)
253  ("rand: invalid distribution ID = %d", id);
254  break;
255  }
256  }
257 
259  {
261 
262  F77_FUNC (setcgn, SETCGN) (uniform_dist);
263  }
264 
266  {
268 
269  F77_FUNC (setcgn, SETCGN) (normal_dist);
270  }
271 
273  {
275 
276  F77_FUNC (setcgn, SETCGN) (expon_dist);
277  }
278 
280  {
282 
283  F77_FUNC (setcgn, SETCGN) (poisson_dist);
284  }
285 
287  {
289 
290  F77_FUNC (setcgn, SETCGN) (gamma_dist);
291  }
292 
293  template <>
294  double rand::uniform<double> (void)
295  {
296  double retval;
297 
298  if (use_old_generators)
299  F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, retval);
300  else
302 
303  return retval;
304  }
305 
306  template <>
307  double rand::normal<double> (void)
308  {
309  double retval;
310 
311  if (use_old_generators)
312  F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, retval);
313  else
315 
316  return retval;
317  }
318 
319  template <>
320  double rand::exponential<double> (void)
321  {
322  double retval;
323 
324  if (use_old_generators)
325  F77_FUNC (dgenexp, DGENEXP) (1.0, retval);
326  else
328 
329  return retval;
330  }
331 
332  template <>
333  double rand::poisson<double> (double a)
334  {
335  double retval;
336 
337  if (use_old_generators)
338  {
339  if (a < 0.0 || ! math::isfinite (a))
341  else
342  {
343  // workaround bug in ignpoi, by calling with different Mu
344  F77_FUNC (dignpoi, DIGNPOI) (a + 1, retval);
345  F77_FUNC (dignpoi, DIGNPOI) (a, retval);
346  }
347  }
348  else
350 
351  return retval;
352  }
353 
354  template <>
355  double rand::gamma<double> (double a)
356  {
357  double retval;
358 
359  if (use_old_generators)
360  {
361  if (a <= 0.0 || ! math::isfinite (a))
363  else
364  F77_FUNC (dgengam, DGENGAM) (1.0, a, retval);
365  }
366  else
367  retval = rand_gamma<double> (a);
368 
369  return retval;
370  }
371 
372  template <>
373  float rand::uniform<float> (void)
374  {
375  float retval;
376 
377  if (use_old_generators)
378  F77_FUNC (fgenunf, FGENUNF) (0.0f, 1.0f, retval);
379  else
381 
382  return retval;
383  }
384 
385  template <>
386  float rand::normal<float> (void)
387  {
388  float retval;
389 
390  if (use_old_generators)
391  F77_FUNC (fgennor, FGENNOR) (0.0f, 1.0f, retval);
392  else
394 
395  return retval;
396  }
397 
398  template <>
399  float rand::exponential<float> (void)
400  {
401  float retval;
402 
403  if (use_old_generators)
404  F77_FUNC (fgenexp, FGENEXP) (1.0f, retval);
405  else
407 
408  return retval;
409  }
410 
411  template <>
412  float rand::poisson<float> (float a)
413  {
414  float retval;
415 
416  if (use_old_generators)
417  {
418  if (a < 0.0f || ! math::isfinite (a))
420  else
421  {
422  // workaround bug in ignpoi, by calling with different Mu
423  F77_FUNC (fignpoi, FIGNPOI) (a + 1, retval);
424  F77_FUNC (fignpoi, FIGNPOI) (a, retval);
425  }
426  }
427  else
428  {
429  // Keep poisson distribution in double precision for accuracy
431  }
432 
433  return retval;
434  }
435 
436  template <>
437  float rand::gamma<float> (float a)
438  {
439  float retval;
440 
441  if (use_old_generators)
442  {
443  if (a <= 0.0f || ! math::isfinite (a))
445  else
446  F77_FUNC (fgengam, FGENGAM) (1.0f, a, retval);
447  }
448  else
449  retval = rand_gamma<float> (a);
450 
451  return retval;
452  }
453 
454  template <typename T>
456  {
457  T retval = 0;
458 
459  switch (current_distribution)
460  {
461  case uniform_dist:
462  retval = uniform<T> ();
463  break;
464 
465  case normal_dist:
466  retval = normal<T> ();
467  break;
468 
469  case expon_dist:
470  retval = exponential<T> ();
471  break;
472 
473  case poisson_dist:
474  retval = poisson<T> (a);
475  break;
476 
477  case gamma_dist:
478  retval = gamma<T> (a);
479  break;
480 
481  default:
482  (*current_liboctave_error_handler)
483  ("rand: invalid distribution ID = %d", current_distribution);
484  break;
485  }
486 
487  if (! use_old_generators)
488  save_state ();
489 
490  return retval;
491  }
492 
493  template double rand::do_scalar<double> (double);
494  template float rand::do_scalar<float> (float);
495 
496  template <typename T>
497  Array<T>
499  {
501 
502  if (n > 0)
503  {
504  retval.clear (n, 1);
505 
506  fill (retval.numel (), retval.fortran_vec (), a);
507  }
508  else if (n < 0)
509  (*current_liboctave_error_handler) ("rand: invalid negative argument");
510 
511  return retval;
512  }
513 
514  template Array<double> rand::do_vector<double> (octave_idx_type, double);
515  template Array<float> rand::do_vector<float> (octave_idx_type, float);
516 
517  NDArray rand::do_nd_array (const dim_vector& dims, double a)
518  {
519  NDArray retval;
520 
521  if (! dims.all_zero ())
522  {
523  retval.clear (dims);
524 
525  fill (retval.numel (), retval.fortran_vec (), a);
526  }
527 
528  return retval;
529  }
530 
532  {
534 
535  if (! dims.all_zero ())
536  {
537  retval.clear (dims);
538 
539  fill (retval.numel (), retval.fortran_vec (), a);
540  }
541 
542  return retval;
543  }
544 
545  // Make the random number generator give us a different sequence every
546  // time we start octave unless we specifically set the seed. The
547  // technique used below will cycle monthly, but it does seem to
548  // work ok to give fairly different seeds each time Octave starts.
549 
551  {
552  sys::localtime tm;
553  int stored_distribution = current_distribution;
554  F77_FUNC (setcgn, SETCGN) (uniform_dist);
555 
556  int hour = tm.hour () + 1;
557  int minute = tm.min () + 1;
558  int second = tm.sec () + 1;
559 
560  int32_t s0 = tm.mday () * hour * minute * second;
561  int32_t s1 = hour * minute * second;
562 
563  s0 = force_to_fit_range (s0, 1, 2147483563);
564  s1 = force_to_fit_range (s1, 1, 2147483399);
565 
566  F77_FUNC (setall, SETALL) (s0, s1);
567  F77_FUNC (setcgn, SETCGN) (stored_distribution);
568  }
569 
571  {
572  uint32NDArray s;
573 
575  s = get_internal_state ();
577 
579  s = get_internal_state ();
581 
583  s = get_internal_state ();
584  rand_states[expon_dist] = s;
585 
587  s = get_internal_state ();
589 
591  s = get_internal_state ();
592  rand_states[gamma_dist] = s;
593 
594  // All of the initializations above have messed with the internal state.
595  // Restore the state of the currently selected distribution.
597  }
598 
600  {
601  uint32NDArray s (dim_vector (MT_N + 1, 1));
602 
603  get_mersenne_twister_state (reinterpret_cast<uint32_t *> (s.fortran_vec ()));
604 
605  return s;
606  }
607 
608  void rand::save_state (void)
609  {
611  }
612 
613  int rand::get_dist_id (const std::string& d)
614  {
615  int retval = unknown_dist;
616 
617  if (d == "uniform" || d == "rand")
619  else if (d == "normal" || d == "randn")
621  else if (d == "exponential" || d == "rande")
622  retval = expon_dist;
623  else if (d == "poisson" || d == "randp")
625  else if (d == "gamma" || d == "randg")
626  retval = gamma_dist;
627  else
629  ("rand: invalid distribution '%s'", d.c_str ());
630 
631  return retval;
632  }
633 
635  {
636  octave_idx_type len = s.numel ();
637 
638  const uint32_t *sdata = reinterpret_cast <const uint32_t *> (s.data ());
639 
640  if (len == MT_N + 1 && sdata[MT_N] <= MT_N && sdata[MT_N] > 0)
642  else
643  init_mersenne_twister (sdata, len);
644  }
645 
647  {
648  if (dist != current_distribution)
649  {
650  current_distribution = dist;
651 
653  }
654  }
655 
656  void rand::fill (octave_idx_type len, double *v, double a)
657  {
658  if (len < 1)
659  return;
660 
661  switch (current_distribution)
662  {
663  case uniform_dist:
664  if (use_old_generators)
665  std::generate_n (v, len, [](void) { double x; F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, x); return x; });
666  else
668  break;
669 
670  case normal_dist:
671  if (use_old_generators)
672  std::generate_n (v, len, [](void) { double x; F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, x); return x; });
673  else
675  break;
676 
677  case expon_dist:
678  if (use_old_generators)
679  std::generate_n (v, len, [](void) { double x; F77_FUNC (dgenexp, DGENEXP) (1.0, x); return x; });
680  else
682  break;
683 
684  case poisson_dist:
685  if (use_old_generators)
686  {
687  if (a < 0.0 || ! math::isfinite (a))
688  std::fill_n (v, len, numeric_limits<double>::NaN ());
689  else
690  {
691  // workaround bug in ignpoi, by calling with different Mu
692  double tmp;
693  F77_FUNC (dignpoi, DIGNPOI) (a + 1, tmp);
694  std::generate_n (v, len, [a](void) { double x; F77_FUNC (dignpoi, DIGNPOI) (a, x); return x; });
695  }
696  }
697  else
698  rand_poisson<double> (a, len, v);
699  break;
700 
701  case gamma_dist:
702  if (use_old_generators)
703  {
704  if (a <= 0.0 || ! math::isfinite (a))
705  std::fill_n (v, len, numeric_limits<double>::NaN ());
706  else
707  std::generate_n (v, len, [a](void) { double x; F77_FUNC (dgengam, DGENGAM) (1.0, a, x); return x; });
708  }
709  else
710  rand_gamma<double> (a, len, v);
711  break;
712 
713  default:
714  (*current_liboctave_error_handler)
715  ("rand: invalid distribution ID = %d", current_distribution);
716  break;
717  }
718 
719  save_state ();
720 
721  return;
722  }
723 
724  void rand::fill (octave_idx_type len, float *v, float a)
725  {
726  if (len < 1)
727  return;
728 
729  switch (current_distribution)
730  {
731  case uniform_dist:
732  if (use_old_generators)
733  std::generate_n (v, len, [](void) { float x; F77_FUNC (fgenunf, FGENUNF) (0.0f, 1.0f, x); return x; });
734  else
736  break;
737 
738  case normal_dist:
739  if (use_old_generators)
740  std::generate_n (v, len, [](void) { float x; F77_FUNC (fgennor, FGENNOR) (0.0f, 1.0f, x); return x; });
741  else
742  rand_normal<float> (len, v);
743  break;
744 
745  case expon_dist:
746  if (use_old_generators)
747  std::generate_n (v, len, [](void) { float x; F77_FUNC (fgenexp, FGENEXP) (1.0f, x); return x; });
748  else
750  break;
751 
752  case poisson_dist:
753  if (use_old_generators)
754  {
755  if (a < 0.0f || ! math::isfinite (a))
756  std::fill_n (v, len, numeric_limits<float>::NaN ());
757  else
758  {
759  // workaround bug in ignpoi, by calling with different Mu
760  float tmp;
761  F77_FUNC (fignpoi, FIGNPOI) (a + 1, tmp);
762  std::generate_n (v, len, [a](void) { float x; F77_FUNC (fignpoi, FIGNPOI) (a, x); return x; });
763  }
764  }
765  else
766  rand_poisson<float> (a, len, v);
767  break;
768 
769  case gamma_dist:
770  if (use_old_generators)
771  {
772  if (a <= 0.0f || ! math::isfinite (a))
773  std::fill_n (v, len, numeric_limits<float>::NaN ());
774  else
775  std::generate_n (v, len, [a](void) { float x; F77_FUNC (fgengam, FGENGAM) (1.0f, a, x); return x; });
776  }
777  else
778  rand_gamma<float> (a, len, v);
779  break;
780 
781  default:
782  (*current_liboctave_error_handler)
783  ("rand: invalid distribution ID = %d", current_distribution);
784  break;
785  }
786 
787  save_state ();
788 
789  return;
790  }
791 }
#define NaN
Definition: Faddeeva.cc:248
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:128
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:581
void clear(void)
Definition: Array.cc:87
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
bool all_zero(void) const
Definition: dim-vector.h:366
void do_normal_distribution(void)
Definition: oct-rand.cc:265
int get_dist_id(const std::string &d)
Definition: oct-rand.cc:613
T do_scalar(T a=1)
Definition: oct-rand.cc:455
void save_state(void)
Definition: oct-rand.cc:608
void do_exponential_distribution(void)
Definition: oct-rand.cc:272
void do_poisson_distribution(void)
Definition: oct-rand.cc:279
rand(void)
Definition: oct-rand.cc:53
std::map< int, uint32NDArray > rand_states
Definition: oct-rand.h:207
Array< T > do_vector(octave_idx_type n, T a=1)
Definition: oct-rand.cc:498
static void cleanup_instance(void)
Definition: oct-rand.h:187
void fill(octave_idx_type len, double *v, double a)
Definition: oct-rand.cc:656
static void normal_distribution(void)
Definition: oct-rand.h:118
void initialize_mersenne_twister(void)
Definition: oct-rand.cc:570
uint32NDArray get_internal_state(void)
Definition: oct-rand.cc:599
static rand * instance
Definition: oct-rand.h:185
void set_internal_state(const uint32NDArray &s)
Definition: oct-rand.cc:634
static void poisson_distribution(void)
Definition: oct-rand.h:130
void do_gamma_distribution(void)
Definition: oct-rand.cc:286
void do_reset()
Definition: oct-rand.cc:138
uint32NDArray do_state(const std::string &d)
Definition: oct-rand.cc:144
int current_distribution
Definition: oct-rand.h:200
bool use_old_generators
Definition: oct-rand.h:204
void switch_to_generator(int dist)
Definition: oct-rand.cc:646
double do_seed(void)
Definition: oct-rand.cc:75
static void exponential_distribution(void)
Definition: oct-rand.h:124
FloatNDArray do_float_nd_array(const dim_vector &dims, float a=1.)
Definition: oct-rand.cc:531
std::string do_distribution(void)
Definition: oct-rand.cc:190
static void gamma_distribution(void)
Definition: oct-rand.h:136
void initialize_ranlib_generators(void)
Definition: oct-rand.cc:550
static void uniform_distribution(void)
Definition: oct-rand.h:112
void do_uniform_distribution(void)
Definition: oct-rand.cc:258
NDArray do_nd_array(const dim_vector &dims, double a=1.)
Definition: oct-rand.cc:517
static bool instance_ok(void)
Definition: oct-rand.cc:62
int hour(void) const
Definition: oct-time.h:232
int mday(void) const
Definition: oct-time.h:233
int min(void) const
Definition: oct-time.h:231
int sec(void) const
Definition: oct-time.h:230
static void add(fptr f)
subroutine getsd(iseed1, iseed2)
Definition: getsd.f:2
OCTAVE_NORETURN liboctave_error_handler current_liboctave_error_handler
Definition: lo-error.c:41
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
float_format native_float_format(void)
Definition: mach-info.cc:65
bool isfinite(double x)
Definition: lo-mappers.h:192
static int32_t force_to_fit_range(int32_t i, int32_t lo, int32_t hi)
Definition: oct-rand.cc:97
double rand_normal< double >(void)
Definition: randmtzig.cc:568
void set_mersenne_twister_state(const uint32_t *save)
Definition: randmtzig.cc:294
template void rand_poisson< float >(float, octave_idx_type, float *)
float rand_normal< float >(void)
Definition: randmtzig.cc:770
template void rand_poisson< double >(double, octave_idx_type, double *)
double rand_exponential< double >(void)
Definition: randmtzig.cc:632
float rand_exponential< float >(void)
Definition: randmtzig.cc:810
void init_mersenne_twister(const uint32_t s)
Definition: randmtzig.cc:197
float rand_uniform< float >(void)
Definition: randmtzig.cc:422
void get_mersenne_twister_state(uint32_t *save)
Definition: randmtzig.cc:301
double rand_uniform< double >(void)
Definition: randmtzig.cc:414
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
static std::string current_distribution
Definition: rand.cc:544
#define MT_N
Definition: randmtzig.h:72
subroutine setall(iseed1, iseed2)
Definition: setall.f:2
subroutine setsd(iseed1, iseed2)
Definition: setsd.f:2
subroutine fignpoi(mu, result)
Definition: wrap.f:65
subroutine fgennor(av, sd, result)
Definition: wrap.f:37
subroutine dgenunf(low, high, result)
Definition: wrap.f:9
subroutine fgenexp(av, result)
Definition: wrap.f:51
subroutine dgengam(a, r, result)
Definition: wrap.f:23
subroutine dgenexp(av, result)
Definition: wrap.f:16
subroutine fgengam(a, r, result)
Definition: wrap.f:58
subroutine fgenunf(low, high, result)
Definition: wrap.f:44
subroutine dgennor(av, sd, result)
Definition: wrap.f:2
subroutine dignpoi(mu, result)
Definition: wrap.f:30
F77_RET_T F77_FUNC(xerbla, XERBLA)(F77_CONST_CHAR_ARG_DEF(s_arg
F77_RET_T len
Definition: xerbla.cc:61