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