oct-rand.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2003-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <map>
00028 #include <vector>
00029 
00030 #include <stdint.h>
00031 
00032 #include "data-conv.h"
00033 #include "f77-fcn.h"
00034 #include "lo-error.h"
00035 #include "lo-ieee.h"
00036 #include "lo-mappers.h"
00037 #include "mach-info.h"
00038 #include "oct-locbuf.h"
00039 #include "oct-rand.h"
00040 #include "oct-time.h"
00041 #include "randgamma.h"
00042 #include "randmtzig.h"
00043 #include "randpoisson.h"
00044 #include "singleton-cleanup.h"
00045 
00046 extern "C"
00047 {
00048   F77_RET_T
00049   F77_FUNC (dgennor, DGENNOR) (const double&, const double&, double&);
00050 
00051   F77_RET_T
00052   F77_FUNC (dgenunf, DGENUNF) (const double&, const double&, double&);
00053 
00054   F77_RET_T
00055   F77_FUNC (dgenexp, DGENEXP) (const double&, double&);
00056 
00057   F77_RET_T
00058   F77_FUNC (dignpoi, DIGNPOI) (const double&, double&);
00059 
00060   F77_RET_T
00061   F77_FUNC (dgengam, DGENGAM) (const double&, const double&, double&);
00062 
00063   F77_RET_T
00064   F77_FUNC (setall, SETALL) (const int32_t&, const int32_t&);
00065 
00066   F77_RET_T
00067   F77_FUNC (getsd, GETSD) (int32_t&, int32_t&);
00068 
00069   F77_RET_T
00070   F77_FUNC (setsd, SETSD) (const int32_t&, const int32_t&);
00071 
00072   F77_RET_T
00073   F77_FUNC (setcgn, SETCGN) (const int32_t&);
00074 }
00075 
00076 octave_rand *octave_rand::instance = 0;
00077 
00078 octave_rand::octave_rand (void)
00079   : current_distribution (uniform_dist), use_old_generators (false),
00080     rand_states ()
00081 {
00082   initialize_ranlib_generators ();
00083 
00084   initialize_mersenne_twister ();
00085 }
00086 
00087 bool
00088 octave_rand::instance_ok (void)
00089 {
00090   bool retval = true;
00091 
00092   if (! instance)
00093     {
00094       instance = new octave_rand ();
00095 
00096       if (instance)
00097         singleton_cleanup_list::add (cleanup_instance);
00098     }
00099 
00100   if (! instance)
00101     {
00102       (*current_liboctave_error_handler)
00103         ("unable to create octave_rand object!");
00104 
00105       retval = false;
00106     }
00107 
00108   return retval;
00109 }
00110 
00111 double
00112 octave_rand::do_seed (void)
00113 {
00114   union d2i { double d; int32_t i[2]; };
00115   union d2i u;
00116 
00117   oct_mach_info::float_format ff = oct_mach_info::native_float_format ();
00118 
00119   switch (ff)
00120     {
00121     case oct_mach_info::flt_fmt_ieee_big_endian:
00122       F77_FUNC (getsd, GETSD) (u.i[1], u.i[0]);
00123       break;
00124     default:
00125       F77_FUNC (getsd, GETSD) (u.i[0], u.i[1]);
00126       break;
00127     }
00128 
00129   return u.d;
00130 }
00131 
00132 static int32_t
00133 force_to_fit_range (int32_t i, int32_t lo, int32_t hi)
00134 {
00135   assert (hi > lo && lo >= 0 && hi > lo);
00136 
00137   i = i > 0 ? i : -i;
00138 
00139   if (i < lo)
00140     i = lo;
00141   else if (i > hi)
00142     i = i % hi;
00143 
00144   return i;
00145 }
00146 
00147 void
00148 octave_rand::do_seed (double s)
00149 {
00150   use_old_generators = true;
00151 
00152   int i0, i1;
00153   union d2i { double d; int32_t i[2]; };
00154   union d2i u;
00155   u.d = s;
00156 
00157   oct_mach_info::float_format ff = oct_mach_info::native_float_format ();
00158 
00159   switch (ff)
00160     {
00161     case oct_mach_info::flt_fmt_ieee_big_endian:
00162       i1 = force_to_fit_range (u.i[0], 1, 2147483563);
00163       i0 = force_to_fit_range (u.i[1], 1, 2147483399);
00164       break;
00165     default:
00166       i0 = force_to_fit_range (u.i[0], 1, 2147483563);
00167       i1 = force_to_fit_range (u.i[1], 1, 2147483399);
00168       break;
00169     }
00170 
00171   F77_FUNC (setsd, SETSD) (i0, i1);
00172 }
00173 
00174 void
00175 octave_rand::do_reset (void)
00176 {
00177   use_old_generators = true;
00178   initialize_ranlib_generators ();
00179 }
00180 
00181 ColumnVector
00182 octave_rand::do_state (const std::string& d)
00183 {
00184   return rand_states[d.empty () ? current_distribution : get_dist_id (d)];
00185 }
00186 
00187 void
00188 octave_rand::do_state (const ColumnVector& s, const std::string& d)
00189 {
00190   use_old_generators = false;
00191 
00192   int old_dist = current_distribution;
00193 
00194   int new_dist = d.empty () ? current_distribution : get_dist_id (d);
00195 
00196   ColumnVector saved_state;
00197 
00198   if (old_dist != new_dist)
00199     saved_state = get_internal_state ();
00200 
00201   set_internal_state (s);
00202 
00203   rand_states[new_dist] = get_internal_state ();
00204 
00205   if (old_dist != new_dist)
00206     rand_states[old_dist] = saved_state;
00207 }
00208 
00209 void
00210 octave_rand::do_reset (const std::string& d)
00211 {
00212   use_old_generators = false;
00213 
00214   int old_dist = current_distribution;
00215 
00216   int new_dist = d.empty () ? current_distribution : get_dist_id (d);
00217 
00218   ColumnVector saved_state;
00219 
00220   if (old_dist != new_dist)
00221     saved_state = get_internal_state ();
00222 
00223   oct_init_by_entropy ();
00224   rand_states[new_dist] = get_internal_state ();
00225 
00226   if (old_dist != new_dist)
00227     rand_states[old_dist] = saved_state;
00228 }
00229 
00230 std::string
00231 octave_rand::do_distribution (void)
00232 {
00233   std::string retval;
00234 
00235   switch (current_distribution)
00236     {
00237     case uniform_dist:
00238       retval = "uniform";
00239       break;
00240 
00241     case normal_dist:
00242       retval = "normal";
00243       break;
00244 
00245     case expon_dist:
00246       retval = "exponential";
00247       break;
00248 
00249     case poisson_dist:
00250       retval = "poisson";
00251       break;
00252 
00253     case gamma_dist:
00254       retval = "gamma";
00255       break;
00256 
00257     default:
00258       (*current_liboctave_error_handler)
00259         ("rand: invalid distribution ID = %d", current_distribution);
00260       break;
00261     }
00262 
00263   return retval;
00264 }
00265 
00266 void
00267 octave_rand::do_distribution (const std::string& d)
00268 {
00269   int id = get_dist_id (d);
00270 
00271   switch (id)
00272     {
00273     case uniform_dist:
00274       octave_rand::uniform_distribution ();
00275       break;
00276 
00277     case normal_dist:
00278       octave_rand::normal_distribution ();
00279       break;
00280 
00281     case expon_dist:
00282       octave_rand::exponential_distribution ();
00283       break;
00284 
00285     case poisson_dist:
00286       octave_rand::poisson_distribution ();
00287       break;
00288 
00289     case gamma_dist:
00290       octave_rand::gamma_distribution ();
00291       break;
00292 
00293     default:
00294       (*current_liboctave_error_handler)
00295         ("rand: invalid distribution ID = %d", id);
00296       break;
00297     }
00298 }
00299 
00300 void
00301 octave_rand::do_uniform_distribution (void)
00302 {
00303   switch_to_generator (uniform_dist);
00304 
00305   F77_FUNC (setcgn, SETCGN) (uniform_dist);
00306 }
00307 
00308 void
00309 octave_rand::do_normal_distribution (void)
00310 {
00311   switch_to_generator (normal_dist);
00312 
00313   F77_FUNC (setcgn, SETCGN) (normal_dist);
00314 }
00315 
00316 void
00317 octave_rand::do_exponential_distribution (void)
00318 {
00319   switch_to_generator (expon_dist);
00320 
00321   F77_FUNC (setcgn, SETCGN) (expon_dist);
00322 }
00323 
00324 void
00325 octave_rand::do_poisson_distribution (void)
00326 {
00327   switch_to_generator (poisson_dist);
00328 
00329   F77_FUNC (setcgn, SETCGN) (poisson_dist);
00330 }
00331 
00332 void
00333 octave_rand::do_gamma_distribution (void)
00334 {
00335   switch_to_generator (gamma_dist);
00336 
00337   F77_FUNC (setcgn, SETCGN) (gamma_dist);
00338 }
00339 
00340 
00341 double
00342 octave_rand::do_scalar (double a)
00343 {
00344   double retval = 0.0;
00345 
00346   if (use_old_generators)
00347     {
00348       switch (current_distribution)
00349         {
00350         case uniform_dist:
00351           F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, retval);
00352           break;
00353 
00354         case normal_dist:
00355           F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, retval);
00356           break;
00357 
00358         case expon_dist:
00359           F77_FUNC (dgenexp, DGENEXP) (1.0, retval);
00360           break;
00361 
00362         case poisson_dist:
00363           if (a < 0.0 || xisnan(a) || xisinf(a))
00364             retval = octave_NaN;
00365           else
00366             {
00367               // workaround bug in ignpoi, by calling with different Mu
00368               F77_FUNC (dignpoi, DIGNPOI) (a + 1, retval);
00369               F77_FUNC (dignpoi, DIGNPOI) (a, retval);
00370             }
00371           break;
00372 
00373         case gamma_dist:
00374           if (a <= 0.0 || xisnan(a) || xisinf(a))
00375             retval = octave_NaN;
00376           else
00377             F77_FUNC (dgengam, DGENGAM) (1.0, a, retval);
00378           break;
00379 
00380         default:
00381           (*current_liboctave_error_handler)
00382             ("rand: invalid distribution ID = %d", current_distribution);
00383           break;
00384         }
00385     }
00386   else
00387     {
00388       switch (current_distribution)
00389         {
00390         case uniform_dist:
00391           retval = oct_randu ();
00392           break;
00393 
00394         case normal_dist:
00395           retval = oct_randn ();
00396           break;
00397 
00398         case expon_dist:
00399           retval = oct_rande ();
00400           break;
00401 
00402         case poisson_dist:
00403           retval = oct_randp (a);
00404           break;
00405 
00406         case gamma_dist:
00407           retval = oct_randg (a);
00408           break;
00409 
00410         default:
00411           (*current_liboctave_error_handler)
00412             ("rand: invalid distribution ID = %d", current_distribution);
00413           break;
00414         }
00415 
00416       save_state ();
00417     }
00418 
00419   return retval;
00420 }
00421 
00422 Matrix
00423 octave_rand::do_matrix (octave_idx_type n, octave_idx_type m, double a)
00424 {
00425   Matrix retval;
00426 
00427   if (n >= 0 && m >= 0)
00428     {
00429       retval.clear (n, m);
00430 
00431       if (n > 0 && m > 0)
00432         fill (retval.capacity(), retval.fortran_vec(), a);
00433     }
00434   else
00435     (*current_liboctave_error_handler) ("rand: invalid negative argument");
00436 
00437   return retval;
00438 }
00439 
00440 NDArray
00441 octave_rand::do_nd_array (const dim_vector& dims, double a)
00442 {
00443   NDArray retval;
00444 
00445   if (! dims.all_zero ())
00446     {
00447       retval.clear (dims);
00448 
00449       fill (retval.capacity(), retval.fortran_vec(), a);
00450     }
00451 
00452   return retval;
00453 }
00454 
00455 Array<double>
00456 octave_rand::do_vector (octave_idx_type n, double a)
00457 {
00458   Array<double> retval;
00459 
00460   if (n > 0)
00461     {
00462       retval.clear (n, 1);
00463 
00464       fill (retval.capacity (), retval.fortran_vec (), a);
00465     }
00466   else if (n < 0)
00467     (*current_liboctave_error_handler) ("rand: invalid negative argument");
00468 
00469   return retval;
00470 }
00471 
00472 // Make the random number generator give us a different sequence every
00473 // time we start octave unless we specifically set the seed.  The
00474 // technique used below will cycle monthly, but it it does seem to
00475 // work ok to give fairly different seeds each time Octave starts.
00476 
00477 void
00478 octave_rand::initialize_ranlib_generators (void)
00479 {
00480   octave_localtime tm;
00481   int stored_distribution = current_distribution;
00482   F77_FUNC (setcgn, SETCGN) (uniform_dist);
00483 
00484   int hour = tm.hour() + 1;
00485   int minute = tm.min() + 1;
00486   int second = tm.sec() + 1;
00487 
00488   int32_t s0 = tm.mday() * hour * minute * second;
00489   int32_t s1 = hour * minute * second;
00490 
00491   s0 = force_to_fit_range (s0, 1, 2147483563);
00492   s1 = force_to_fit_range (s1, 1, 2147483399);
00493 
00494   F77_FUNC (setall, SETALL) (s0, s1);
00495   F77_FUNC (setcgn, SETCGN) (stored_distribution);
00496 }
00497 
00498 void
00499 octave_rand::initialize_mersenne_twister (void)
00500 {
00501   oct_init_by_entropy ();
00502 
00503   ColumnVector s = get_internal_state ();
00504 
00505   rand_states[uniform_dist] = s;
00506 
00507   oct_init_by_entropy ();
00508   s = get_internal_state ();
00509   rand_states[normal_dist] = s;
00510 
00511   oct_init_by_entropy ();
00512   s = get_internal_state ();
00513   rand_states[expon_dist] = s;
00514 
00515   oct_init_by_entropy ();
00516   s = get_internal_state ();
00517   rand_states[poisson_dist] = s;
00518 
00519   oct_init_by_entropy ();
00520   s = get_internal_state ();
00521   rand_states[gamma_dist] = s;
00522 }
00523 
00524 ColumnVector
00525 octave_rand::get_internal_state (void)
00526 {
00527   ColumnVector s (MT_N + 1);
00528 
00529   OCTAVE_LOCAL_BUFFER (uint32_t, tmp, MT_N + 1);
00530 
00531   oct_get_state (tmp);
00532 
00533   for (octave_idx_type i = 0; i <= MT_N; i++)
00534     s.elem (i) = static_cast<double> (tmp [i]);
00535 
00536   return s;
00537 }
00538 
00539 void
00540 octave_rand::save_state (void)
00541 {
00542   rand_states[current_distribution] = get_internal_state ();;
00543 }
00544 
00545 int
00546 octave_rand::get_dist_id (const std::string& d)
00547 {
00548   int retval = unknown_dist;
00549 
00550   if (d == "uniform" || d == "rand")
00551     retval = uniform_dist;
00552   else if (d == "normal" || d == "randn")
00553     retval = normal_dist;
00554   else if (d == "exponential" || d == "rande")
00555     retval = expon_dist;
00556   else if (d == "poisson" || d == "randp")
00557     retval = poisson_dist;
00558   else if (d == "gamma" || d == "randg")
00559     retval = gamma_dist;
00560   else
00561     (*current_liboctave_error_handler)
00562       ("rand: invalid distribution '%s'", d.c_str ());
00563 
00564   return retval;
00565 }
00566 
00567 void
00568 octave_rand::set_internal_state (const ColumnVector& s)
00569 {
00570   octave_idx_type len = s.length ();
00571   octave_idx_type n = len < MT_N + 1 ? len : MT_N + 1;
00572 
00573   OCTAVE_LOCAL_BUFFER (uint32_t, tmp, MT_N + 1);
00574 
00575   for (octave_idx_type i = 0; i < n; i++)
00576     tmp[i] = static_cast<uint32_t> (s.elem(i));
00577 
00578   if (len == MT_N + 1 && tmp[MT_N] <= MT_N && tmp[MT_N] > 0)
00579     oct_set_state (tmp);
00580   else
00581     oct_init_by_array (tmp, len);
00582 }
00583 
00584 void
00585 octave_rand::switch_to_generator (int dist)
00586 {
00587   if (dist != current_distribution)
00588     {
00589       current_distribution = dist;
00590 
00591       set_internal_state (rand_states[dist]);
00592     }
00593 }
00594 
00595 #define MAKE_RAND(len) \
00596   do \
00597     { \
00598       double val; \
00599       for (volatile octave_idx_type i = 0; i < len; i++) \
00600         { \
00601           octave_quit (); \
00602           RAND_FUNC (val); \
00603           v[i] = val; \
00604         } \
00605     } \
00606   while (0)
00607 
00608 void
00609 octave_rand::fill (octave_idx_type len, double *v, double a)
00610 {
00611   if (len < 1)
00612     return;
00613 
00614   switch (current_distribution)
00615     {
00616     case uniform_dist:
00617       if (use_old_generators)
00618         {
00619 #define RAND_FUNC(x) F77_FUNC (dgenunf, DGENUNF) (0.0, 1.0, x)
00620           MAKE_RAND (len);
00621 #undef RAND_FUNC
00622         }
00623       else
00624         oct_fill_randu (len, v);
00625       break;
00626 
00627     case normal_dist:
00628       if (use_old_generators)
00629         {
00630 #define RAND_FUNC(x) F77_FUNC (dgennor, DGENNOR) (0.0, 1.0, x)
00631           MAKE_RAND (len);
00632 #undef RAND_FUNC
00633         }
00634       else
00635         oct_fill_randn (len, v);
00636       break;
00637 
00638     case expon_dist:
00639       if (use_old_generators)
00640         {
00641 #define RAND_FUNC(x) F77_FUNC (dgenexp, DGENEXP) (1.0, x)
00642           MAKE_RAND (len);
00643 #undef RAND_FUNC
00644         }
00645       else
00646         oct_fill_rande (len, v);
00647       break;
00648 
00649     case poisson_dist:
00650       if (use_old_generators)
00651         {
00652           if (a < 0.0 || xisnan(a) || xisinf(a))
00653 #define RAND_FUNC(x) x = octave_NaN;
00654             MAKE_RAND (len);
00655 #undef RAND_FUNC
00656           else
00657             {
00658               // workaround bug in ignpoi, by calling with different Mu
00659               double tmp;
00660               F77_FUNC (dignpoi, DIGNPOI) (a + 1, tmp);
00661 #define RAND_FUNC(x) F77_FUNC (dignpoi, DIGNPOI) (a, x)
00662                 MAKE_RAND (len);
00663 #undef RAND_FUNC
00664             }
00665         }
00666       else
00667         oct_fill_randp (a, len, v);
00668       break;
00669 
00670     case gamma_dist:
00671       if (use_old_generators)
00672         {
00673           if (a <= 0.0 || xisnan(a) || xisinf(a))
00674 #define RAND_FUNC(x) x = octave_NaN;
00675             MAKE_RAND (len);
00676 #undef RAND_FUNC
00677           else
00678 #define RAND_FUNC(x) F77_FUNC (dgengam, DGENGAM) (1.0, a, x)
00679             MAKE_RAND (len);
00680 #undef RAND_FUNC
00681         }
00682       else
00683         oct_fill_randg (a, len, v);
00684       break;
00685 
00686     default:
00687       (*current_liboctave_error_handler)
00688         ("rand: invalid distribution ID = %d", current_distribution);
00689       break;
00690     }
00691 
00692   save_state ();
00693 
00694   return;
00695 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines