GNU Octave 7.1.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-2022 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
49namespace octave
50{
51 rand *rand::m_instance = nullptr;
52
54 : m_current_distribution (uniform_dist), m_use_old_generators (false),
55 m_rand_states ()
56 {
58
60 }
61
63 {
64 bool retval = true;
65
66 if (! m_instance)
67 {
68 m_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 {
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 {
142 }
143
144 uint32NDArray rand::do_state (const std::string& d)
145 {
146 return m_rand_states[d.empty () ? m_current_distribution : get_dist_id (d)];
147 }
148
149 void rand::do_state (const uint32NDArray& s, const std::string& d)
150 {
151 m_use_old_generators = false;
152
153 int old_dist = m_current_distribution;
154
155 int new_dist = (d.empty () ? m_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
163
164 m_rand_states[new_dist] = get_internal_state ();
165
166 if (old_dist != new_dist)
167 m_rand_states[old_dist] = saved_state;
168 }
169
170 void rand::do_reset (const std::string& d)
171 {
172 m_use_old_generators = false;
173
174 int old_dist = m_current_distribution;
175
176 int new_dist = (d.empty () ? m_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 m_rand_states[new_dist] = get_internal_state ();
185
186 if (old_dist != new_dist)
187 m_rand_states[old_dist] = saved_state;
188 }
189
190 std::string rand::do_distribution (void)
191 {
192 std::string retval;
193
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", m_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 OCTAVE_API double rand::uniform<double> (void)
295 {
296 double retval;
297
299 F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, retval);
300 else
301 retval = rand_uniform<double> ();
302
303 return retval;
304 }
305
306 template <>
307 OCTAVE_API double rand::normal<double> (void)
308 {
309 double retval;
310
312 F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, retval);
313 else
314 retval = rand_normal<double> ();
315
316 return retval;
317 }
318
319 template <>
320 OCTAVE_API double rand::exponential<double> (void)
321 {
322 double retval;
323
325 F77_FUNC (dgenexp, DGENEXP) (1.0, retval);
326 else
327 retval = rand_exponential<double> ();
328
329 return retval;
330 }
331
332 template <>
333 OCTAVE_API double rand::poisson<double> (double a)
334 {
335 double retval;
336
338 {
339 if (a < 0.0 || ! math::isfinite (a))
340 retval = numeric_limits<double>::NaN ();
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
349 retval = rand_poisson<double> (a);
350
351 return retval;
352 }
353
354 template <>
355 OCTAVE_API double rand::gamma<double> (double a)
356 {
357 double retval;
358
360 {
361 if (a <= 0.0 || ! math::isfinite (a))
362 retval = numeric_limits<double>::NaN ();
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 OCTAVE_API float rand::uniform<float> (void)
374 {
375 float retval;
376
378 F77_FUNC (fgenunf, FGENUNF) (0.0f, 1.0f, retval);
379 else
380 retval = rand_uniform<float> ();
381
382 return retval;
383 }
384
385 template <>
386 OCTAVE_API float rand::normal<float> (void)
387 {
388 float retval;
389
391 F77_FUNC (fgennor, FGENNOR) (0.0f, 1.0f, retval);
392 else
393 retval = rand_normal<float> ();
394
395 return retval;
396 }
397
398 template <>
399 OCTAVE_API float rand::exponential<float> (void)
400 {
401 float retval;
402
404 F77_FUNC (fgenexp, FGENEXP) (1.0f, retval);
405 else
406 retval = rand_exponential<float> ();
407
408 return retval;
409 }
410
411 template <>
412 OCTAVE_API float rand::poisson<float> (float a)
413 {
414 float retval;
415
417 {
418 if (a < 0.0f || ! math::isfinite (a))
419 retval = numeric_limits<float>::NaN ();
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
430 retval = rand_poisson<double> (a);
431 }
432
433 return retval;
434 }
435
436 template <>
437 OCTAVE_API float rand::gamma<float> (float a)
438 {
439 float retval;
440
442 {
443 if (a <= 0.0f || ! math::isfinite (a))
444 retval = numeric_limits<float>::NaN ();
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
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", m_current_distribution);
484 break;
485 }
486
488 save_state ();
489
490 return retval;
491 }
492
493 template OCTAVE_API double rand::do_scalar<double> (double);
494 template OCTAVE_API float rand::do_scalar<float> (float);
495
496 template <typename T>
499 {
500 Array<T> retval;
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
515 rand::do_vector<double> (octave_idx_type, double);
516 template OCTAVE_API Array<float>
517 rand::do_vector<float> (octave_idx_type, float);
518
519 NDArray rand::do_nd_array (const dim_vector& dims, double a)
520 {
521 NDArray retval;
522
523 if (! dims.all_zero ())
524 {
525 retval.clear (dims);
526
527 fill (retval.numel (), retval.fortran_vec (), a);
528 }
529
530 return retval;
531 }
532
534 {
535 FloatNDArray retval;
536
537 if (! dims.all_zero ())
538 {
539 retval.clear (dims);
540
541 fill (retval.numel (), retval.fortran_vec (), a);
542 }
543
544 return retval;
545 }
546
547 // Make the random number generator give us a different sequence every
548 // time we start octave unless we specifically set the seed. The
549 // technique used below will cycle monthly, but it does seem to
550 // work ok to give fairly different seeds each time Octave starts.
551
553 {
555 int stored_distribution = m_current_distribution;
556 F77_FUNC (setcgn, SETCGN) (uniform_dist);
557
558 int hour = tm.hour () + 1;
559 int minute = tm.min () + 1;
560 int second = tm.sec () + 1;
561
562 int32_t s0 = tm.mday () * hour * minute * second;
563 int32_t s1 = hour * minute * second;
564
565 s0 = force_to_fit_range (s0, 1, 2147483563);
566 s1 = force_to_fit_range (s1, 1, 2147483399);
567
568 F77_FUNC (setall, SETALL) (s0, s1);
569 F77_FUNC (setcgn, SETCGN) (stored_distribution);
570 }
571
573 {
575
577 s = get_internal_state ();
579
581 s = get_internal_state ();
583
585 s = get_internal_state ();
587
589 s = get_internal_state ();
591
593 s = get_internal_state ();
595
596 // All of the initializations above have messed with the internal state.
597 // Restore the state of the currently selected distribution.
599 }
600
602 {
603 uint32NDArray s (dim_vector (MT_N + 1, 1));
604
605 get_mersenne_twister_state (reinterpret_cast<uint32_t *> (s.fortran_vec ()));
606
607 return s;
608 }
609
611 {
613 }
614
615 int rand::get_dist_id (const std::string& d)
616 {
617 int retval = unknown_dist;
618
619 if (d == "uniform" || d == "rand")
620 retval = uniform_dist;
621 else if (d == "normal" || d == "randn")
622 retval = normal_dist;
623 else if (d == "exponential" || d == "rande")
624 retval = expon_dist;
625 else if (d == "poisson" || d == "randp")
626 retval = poisson_dist;
627 else if (d == "gamma" || d == "randg")
628 retval = gamma_dist;
629 else
631 ("rand: invalid distribution '%s'", d.c_str ());
632
633 return retval;
634 }
635
637 {
639
640 const uint32_t *sdata = reinterpret_cast <const uint32_t *> (s.data ());
641
642 if (len == MT_N + 1 && sdata[MT_N] <= MT_N && sdata[MT_N] > 0)
644 else
645 init_mersenne_twister (sdata, len);
646 }
647
649 {
650 if (dist != m_current_distribution)
651 {
653
655 }
656 }
657
658 void rand::fill (octave_idx_type len, double *v, double a)
659 {
660 if (len < 1)
661 return;
662
664 {
665 case uniform_dist:
667 std::generate_n (v, len, [](void) { double x; F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, x); return x; });
668 else
670 break;
671
672 case normal_dist:
674 std::generate_n (v, len, [](void) { double x; F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, x); return x; });
675 else
677 break;
678
679 case expon_dist:
681 std::generate_n (v, len, [](void) { double x; F77_FUNC (dgenexp, DGENEXP) (1.0, x); return x; });
682 else
684 break;
685
686 case poisson_dist:
688 {
689 if (a < 0.0 || ! math::isfinite (a))
690 std::fill_n (v, len, numeric_limits<double>::NaN ());
691 else
692 {
693 // workaround bug in ignpoi, by calling with different Mu
694 double tmp;
695 F77_FUNC (dignpoi, DIGNPOI) (a + 1, tmp);
696 std::generate_n (v, len, [a](void) { double x; F77_FUNC (dignpoi, DIGNPOI) (a, x); return x; });
697 }
698 }
699 else
701 break;
702
703 case gamma_dist:
705 {
706 if (a <= 0.0 || ! math::isfinite (a))
707 std::fill_n (v, len, numeric_limits<double>::NaN ());
708 else
709 std::generate_n (v, len, [a](void) { double x; F77_FUNC (dgengam, DGENGAM) (1.0, a, x); return x; });
710 }
711 else
712 rand_gamma<double> (a, len, v);
713 break;
714
715 default:
716 (*current_liboctave_error_handler)
717 ("rand: invalid distribution ID = %d", m_current_distribution);
718 break;
719 }
720
721 save_state ();
722
723 return;
724 }
725
726 void rand::fill (octave_idx_type len, float *v, float a)
727 {
728 if (len < 1)
729 return;
730
732 {
733 case uniform_dist:
735 std::generate_n (v, len, [](void) { float x; F77_FUNC (fgenunf, FGENUNF) (0.0f, 1.0f, x); return x; });
736 else
738 break;
739
740 case normal_dist:
742 std::generate_n (v, len, [](void) { float x; F77_FUNC (fgennor, FGENNOR) (0.0f, 1.0f, x); return x; });
743 else
745 break;
746
747 case expon_dist:
749 std::generate_n (v, len, [](void) { float x; F77_FUNC (fgenexp, FGENEXP) (1.0f, x); return x; });
750 else
752 break;
753
754 case poisson_dist:
756 {
757 if (a < 0.0f || ! math::isfinite (a))
758 std::fill_n (v, len, numeric_limits<float>::NaN ());
759 else
760 {
761 // workaround bug in ignpoi, by calling with different Mu
762 float tmp;
763 F77_FUNC (fignpoi, FIGNPOI) (a + 1, tmp);
764 std::generate_n (v, len, [a](void) { float x; F77_FUNC (fignpoi, FIGNPOI) (a, x); return x; });
765 }
766 }
767 else
768 rand_poisson<float> (a, len, v);
769 break;
770
771 case gamma_dist:
773 {
774 if (a <= 0.0f || ! math::isfinite (a))
775 std::fill_n (v, len, numeric_limits<float>::NaN ());
776 else
777 std::generate_n (v, len, [a](void) { float x; F77_FUNC (fgengam, FGENGAM) (1.0f, a, x); return x; });
778 }
779 else
780 rand_gamma<float> (a, len, v);
781 break;
782
783 default:
784 (*current_liboctave_error_handler)
785 ("rand: invalid distribution ID = %d", m_current_distribution);
786 break;
787 }
788
789 save_state ();
790
791 return;
792 }
793}
#define NaN
Definition: Faddeeva.cc:261
OCTARRAY_API void clear(void)
Definition: Array.cc:87
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:411
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:616
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array.cc:1744
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
bool all_zero(void) const
Definition: dim-vector.h:300
OCTAVE_API void do_normal_distribution(void)
Definition: oct-rand.cc:265
OCTAVE_API int get_dist_id(const std::string &d)
Definition: oct-rand.cc:615
OCTAVE_API void save_state(void)
Definition: oct-rand.cc:610
OCTAVE_API void do_exponential_distribution(void)
Definition: oct-rand.cc:272
OCTAVE_API void do_poisson_distribution(void)
Definition: oct-rand.cc:279
OCTAVE_API rand(void)
Definition: oct-rand.cc:53
bool m_use_old_generators
Definition: oct-rand.h:205
static void cleanup_instance(void)
Definition: oct-rand.h:187
OCTAVE_API Array< T > do_vector(octave_idx_type n, T a=1)
OCTAVE_API void fill(octave_idx_type len, double *v, double a)
Definition: oct-rand.cc:658
static void normal_distribution(void)
Definition: oct-rand.h:118
OCTAVE_API void initialize_mersenne_twister(void)
Definition: oct-rand.cc:572
OCTAVE_API uint32NDArray get_internal_state(void)
Definition: oct-rand.cc:601
OCTAVE_API void set_internal_state(const uint32NDArray &s)
Definition: oct-rand.cc:636
static void poisson_distribution(void)
Definition: oct-rand.h:130
OCTAVE_API void do_gamma_distribution(void)
Definition: oct-rand.cc:286
OCTAVE_API void do_reset()
Definition: oct-rand.cc:138
OCTAVE_API uint32NDArray do_state(const std::string &d)
Definition: oct-rand.cc:144
static rand * m_instance
Definition: oct-rand.h:185
std::map< int, uint32NDArray > m_rand_states
Definition: oct-rand.h:208
int m_current_distribution
Definition: oct-rand.h:201
OCTAVE_API void switch_to_generator(int dist)
Definition: oct-rand.cc:648
OCTAVE_API double do_seed(void)
Definition: oct-rand.cc:75
static void exponential_distribution(void)
Definition: oct-rand.h:124
OCTAVE_API FloatNDArray do_float_nd_array(const dim_vector &dims, float a=1.)
Definition: oct-rand.cc:533
OCTAVE_API std::string do_distribution(void)
Definition: oct-rand.cc:190
static void gamma_distribution(void)
Definition: oct-rand.h:136
OCTAVE_API T do_scalar(T a=1)
OCTAVE_API void initialize_ranlib_generators(void)
Definition: oct-rand.cc:552
static void uniform_distribution(void)
Definition: oct-rand.h:112
OCTAVE_API void do_uniform_distribution(void)
Definition: oct-rand.cc:258
OCTAVE_API NDArray do_nd_array(const dim_vector &dims, double a=1.)
Definition: oct-rand.cc:519
static bool instance_ok(void)
Definition: oct-rand.cc:62
int hour(void) const
Definition: oct-time.h:228
int mday(void) const
Definition: oct-time.h:229
int min(void) const
Definition: oct-time.h:227
int sec(void) const
Definition: oct-time.h:226
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
#define OCTAVE_API
Definition: main.in.cc:55
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
OCTAVE_API float rand_uniform< float >(void)
Definition: randmtzig.cc:438
F77_RET_T F77_FUNC(dconv2o, DCONV2O)(const F77_INT &
void get_mersenne_twister_state(uint32_t *save)
Definition: randmtzig.cc:317
template void rand_poisson< float >(float, octave_idx_type, float *)
template void rand_poisson< double >(double, octave_idx_type, double *)
OCTAVE_API double rand_uniform< double >(void)
Definition: randmtzig.cc:430
void init_mersenne_twister(const uint32_t s)
Definition: randmtzig.cc:199
OCTAVE_API double rand_normal< double >(void)
Definition: randmtzig.cc:584
OCTAVE_API float rand_normal< float >(void)
Definition: randmtzig.cc:786
void set_mersenne_twister_state(const uint32_t *save)
Definition: randmtzig.cc:310
OCTAVE_API double rand_exponential< double >(void)
Definition: randmtzig.cc:648
OCTAVE_API float rand_exponential< float >(void)
Definition: randmtzig.cc:826
#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 len
Definition: xerbla.cc:61