Mercurial > octave
diff src/corefcn/rand.cc @ 15039:e753177cde93
maint: Move non-dynamically linked functions from DLD-FUNCTIONS/ to corefcn/ directory
* __contourc__.cc, __dispatch__.cc, __lin_interpn__.cc, __pchip_deriv__.cc,
__qp__.cc, balance.cc, besselj.cc, betainc.cc, bsxfun.cc, cellfun.cc,
colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, det.cc, dlmread.cc, dot.cc,
eig.cc, fft.cc, fft2.cc, fftn.cc, filter.cc, find.cc, gammainc.cc, gcd.cc,
getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, hess.cc, hex2num.cc, inv.cc,
kron.cc, lookup.cc, lsode.cc, lu.cc, luinc.cc, matrix_type.cc, max.cc,
md5sum.cc, mgorth.cc, nproc.cc, pinv.cc, quad.cc, quadcc.cc, qz.cc,
rand.cc, rcond.cc, regexp.cc, schur.cc, spparms.cc, sqrtm.cc, str2double.cc,
strfind.cc, sub2ind.cc, svd.cc, syl.cc, time.cc, tril.cc, typecast.cc:
Move functions from DLD-FUNCTIONS/ to corefcn/ directory. Include "defun.h",
not "defun-dld.h". Change docstring to refer to these as "Built-in Functions".
* build-aux/mk-opts.pl: Generate options code with '#include "defun.h"'. Change
option docstrings to refer to these as "Built-in Functions".
* corefcn/module.mk: List of functions to build in corefcn/ dir.
* DLD-FUNCTIONS/config-module.awk: Update to new build system.
* DLD-FUNCTIONS/module-files: Remove functions which are now in corefcn/ directory.
* src/Makefile.am: Update to build "convenience library" in corefcn/. Octave
program now links against all other libraries + corefcn libary.
* src/find-defun-files.sh: Strip $srcdir from filename.
* src/link-deps.mk: Add REGEX and FFTW link dependencies for liboctinterp.
* type.m, which.m: Change failing tests to use 'amd', still a dynamic function,
rather than 'dot', which isn't.
author | Rik <rik@octave.org> |
---|---|
date | Fri, 27 Jul 2012 15:35:00 -0700 |
parents | src/DLD-FUNCTIONS/rand.cc@5ae9f0f77635 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/corefcn/rand.cc Fri Jul 27 15:35:00 2012 -0700 @@ -0,0 +1,1221 @@ + +/* + +Copyright (C) 1996-2012 John W. Eaton +Copyright (C) 2009 VZLU Prague + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctime> +#if defined (HAVE_UNORDERED_MAP) +#include <unordered_map> +#elif defined (HAVE_TR1_UNORDERED_MAP) +#include <tr1/unordered_map> +#endif +#include <string> + +#include "f77-fcn.h" +#include "lo-mappers.h" +#include "oct-rand.h" +#include "quit.h" + +#include "defun.h" +#include "error.h" +#include "gripes.h" +#include "oct-obj.h" +#include "unwind-prot.h" +#include "utils.h" +#include "ov-re-mat.h" + +/* +%!shared __random_statistical_tests__ +%! # Flag whether the statistical tests should be run in "make check" or not +%! __random_statistical_tests__ = 0; +*/ + +static octave_value +do_rand (const octave_value_list& args, int nargin, const char *fcn, + const std::string& distribution, bool additional_arg = false) +{ + octave_value retval; + NDArray a; + int idx = 0; + dim_vector dims; + bool is_single = false; + + unwind_protect frame; + // Restore current distribution on any exit. + frame.add_fcn (octave_rand::distribution, + octave_rand::distribution ()); + + octave_rand::distribution (distribution); + + if (nargin > 0 && args(nargin-1).is_string ()) + { + std::string s_arg = args(nargin-1).string_value (); + + if (s_arg == "single") + { + is_single = true; + nargin--; + } + else if (s_arg == "double") + nargin--; + } + + if (additional_arg) + { + if (nargin == 0) + { + error ("%s: expecting at least one argument", fcn); + goto done; + } + else if (args(0).is_string ()) + additional_arg = false; + else + { + a = args(0).array_value (); + if (error_state) + { + error ("%s: expecting scalar or matrix arguments", fcn); + goto done; + } + idx++; + nargin--; + } + } + + switch (nargin) + { + case 0: + { + if (additional_arg) + dims = a.dims (); + else + { + dims.resize (2); + + dims(0) = 1; + dims(1) = 1; + } + goto gen_matrix; + } + break; + + case 1: + { + octave_value tmp = args(idx); + + if (tmp.is_string ()) + { + std::string s_arg = tmp.string_value (); + + if (s_arg == "dist") + { + retval = octave_rand::distribution (); + } + else if (s_arg == "seed") + { + retval = octave_rand::seed (); + } + else if (s_arg == "state" || s_arg == "twister") + { + retval = octave_rand::state (fcn); + } + else if (s_arg == "uniform") + { + octave_rand::uniform_distribution (); + } + else if (s_arg == "normal") + { + octave_rand::normal_distribution (); + } + else if (s_arg == "exponential") + { + octave_rand::exponential_distribution (); + } + else if (s_arg == "poisson") + { + octave_rand::poisson_distribution (); + } + else if (s_arg == "gamma") + { + octave_rand::gamma_distribution (); + } + else + error ("%s: unrecognized string argument", fcn); + } + else if (tmp.is_scalar_type ()) + { + double dval = tmp.double_value (); + + if (xisnan (dval)) + { + error ("%s: NaN is invalid matrix dimension", fcn); + } + else + { + dims.resize (2); + + dims(0) = NINTbig (tmp.double_value ()); + dims(1) = NINTbig (tmp.double_value ()); + + if (! error_state) + goto gen_matrix; + } + } + else if (tmp.is_range ()) + { + Range r = tmp.range_value (); + + if (r.all_elements_are_ints ()) + { + octave_idx_type n = r.nelem (); + + dims.resize (n); + + octave_idx_type base = NINTbig (r.base ()); + octave_idx_type incr = NINTbig (r.inc ()); + + for (octave_idx_type i = 0; i < n; i++) + { + //Negative dimensions are treated as zero for Matlab + //compatibility + dims(i) = base >= 0 ? base : 0; + base += incr; + } + + goto gen_matrix; + + } + else + error ("%s: all elements of range must be integers", + fcn); + } + else if (tmp.is_matrix_type ()) + { + Array<int> iv = tmp.int_vector_value (true); + + if (! error_state) + { + octave_idx_type len = iv.length (); + + dims.resize (len); + + for (octave_idx_type i = 0; i < len; i++) + { + //Negative dimensions are treated as zero for Matlab + //compatibility + octave_idx_type elt = iv(i); + dims(i) = elt >=0 ? elt : 0; + } + + goto gen_matrix; + } + else + error ("%s: expecting integer vector", fcn); + } + else + { + gripe_wrong_type_arg ("rand", tmp); + return retval; + } + } + break; + + default: + { + octave_value tmp = args(idx); + + if (nargin == 2 && tmp.is_string ()) + { + std::string ts = tmp.string_value (); + + if (ts == "seed") + { + if (args(idx+1).is_real_scalar ()) + { + double d = args(idx+1).double_value (); + + if (! error_state) + octave_rand::seed (d); + } + else if (args(idx+1).is_string () + && args(idx+1).string_value () == "reset") + octave_rand::reset (); + else + error ("%s: seed must be a real scalar", fcn); + } + else if (ts == "state" || ts == "twister") + { + if (args(idx+1).is_string () + && args(idx+1).string_value () == "reset") + octave_rand::reset (fcn); + else + { + ColumnVector s = + ColumnVector (args(idx+1).vector_value(false, true)); + + if (! error_state) + octave_rand::state (s, fcn); + } + } + else + error ("%s: unrecognized string argument", fcn); + } + else + { + dims.resize (nargin); + + for (int i = 0; i < nargin; i++) + { + octave_idx_type elt = args(idx+i).int_value (); + if (error_state) + { + error ("%s: expecting integer arguments", fcn); + goto done; + } + //Negative is zero for Matlab compatibility + dims(i) = elt >= 0 ? elt : 0; + } + + goto gen_matrix; + } + } + break; + } + + done: + + return retval; + + gen_matrix: + + dims.chop_trailing_singletons (); + + if (is_single) + { + if (additional_arg) + { + if (a.length () == 1) + return octave_rand::float_nd_array (dims, a(0)); + else + { + if (a.dims () != dims) + { + error ("%s: mismatch in argument size", fcn); + return retval; + } + octave_idx_type len = a.length (); + FloatNDArray m (dims); + float *v = m.fortran_vec (); + for (octave_idx_type i = 0; i < len; i++) + v[i] = octave_rand::float_scalar (a(i)); + return m; + } + } + else + return octave_rand::float_nd_array (dims); + } + else + { + if (additional_arg) + { + if (a.length () == 1) + return octave_rand::nd_array (dims, a(0)); + else + { + if (a.dims () != dims) + { + error ("%s: mismatch in argument size", fcn); + return retval; + } + octave_idx_type len = a.length (); + NDArray m (dims); + double *v = m.fortran_vec (); + for (octave_idx_type i = 0; i < len; i++) + v[i] = octave_rand::scalar (a(i)); + return m; + } + } + else + return octave_rand::nd_array (dims); + } +} + +DEFUN (rand, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} rand (@var{n})\n\ +@deftypefnx {Built-in Function} {} rand (@var{n}, @var{m}, @dots{})\n\ +@deftypefnx {Built-in Function} {} rand ([@var{n} @var{m} @dots{}])\n\ +@deftypefnx {Built-in Function} {@var{v} =} rand (\"state\")\n\ +@deftypefnx {Built-in Function} {} rand (\"state\", @var{v})\n\ +@deftypefnx {Built-in Function} {} rand (\"state\", \"reset\")\n\ +@deftypefnx {Built-in Function} {@var{v} =} rand (\"seed\")\n\ +@deftypefnx {Built-in Function} {} rand (\"seed\", @var{v})\n\ +@deftypefnx {Built-in Function} {} rand (\"seed\", \"reset\")\n\ +@deftypefnx {Built-in Function} {} rand (@dots{}, \"single\")\n\ +@deftypefnx {Built-in Function} {} rand (@dots{}, \"double\")\n\ +Return a matrix with random elements uniformly distributed on the\n\ +interval (0, 1). The arguments are handled the same as the arguments\n\ +for @code{eye}.\n\ +\n\ +You can query the state of the random number generator using the\n\ +form\n\ +\n\ +@example\n\ +v = rand (\"state\")\n\ +@end example\n\ +\n\ +This returns a column vector @var{v} of length 625. Later, you can\n\ +restore the random number generator to the state @var{v}\n\ +using the form\n\ +\n\ +@example\n\ +rand (\"state\", v)\n\ +@end example\n\ +\n\ +@noindent\n\ +You may also initialize the state vector from an arbitrary vector of\n\ +length @leq{} 625 for @var{v}. This new state will be a hash based on the\n\ +value of @var{v}, not @var{v} itself.\n\ +\n\ +By default, the generator is initialized from @code{/dev/urandom} if it is\n\ +available, otherwise from CPU time, wall clock time, and the current\n\ +fraction of a second. Note that this differs from @sc{matlab}, which\n\ +always initializes the state to the same state at startup. To obtain\n\ +behavior comparable to @sc{matlab}, initialize with a deterministic state\n\ +vector in Octave's startup files (@pxref{Startup Files}).\n\ +\n\ +To compute the pseudo-random sequence, @code{rand} uses the Mersenne\n\ +Twister with a period of @math{2^{19937}-1} (See M. Matsumoto and\n\ +T. Nishimura,\n\ +@cite{Mersenne Twister: A 623-dimensionally equidistributed uniform\n\ +pseudorandom number generator}, ACM Trans. on\n\ +Modeling and Computer Simulation Vol. 8, No. 1, pp. 3-30, January 1998,\n\ +@url{http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html}).\n\ +Do @strong{not} use for cryptography without securely hashing\n\ +several returned values together, otherwise the generator state\n\ +can be learned after reading 624 consecutive values.\n\ +\n\ +Older versions of Octave used a different random number generator.\n\ +The new generator is used by default\n\ +as it is significantly faster than the old generator, and produces\n\ +random numbers with a significantly longer cycle time. However, in\n\ +some circumstances it might be desirable to obtain the same random\n\ +sequences as used by the old generators. To do this the keyword\n\ +\"seed\" is used to specify that the old generators should be use,\n\ +as in\n\ +\n\ +@example\n\ +rand (\"seed\", val)\n\ +@end example\n\ +\n\ +@noindent\n\ +which sets the seed of the generator to @var{val}. The seed of the\n\ +generator can be queried with\n\ +\n\ +@example\n\ +s = rand (\"seed\")\n\ +@end example\n\ +\n\ +However, it should be noted that querying the seed will not cause\n\ +@code{rand} to use the old generators, only setting the seed will.\n\ +To cause @code{rand} to once again use the new generators, the\n\ +keyword \"state\" should be used to reset the state of the @code{rand}.\n\ +\n\ +The state or seed of the generator can be reset to a new random value\n\ +using the \"reset\" keyword.\n\ +\n\ +The class of the value returned can be controlled by a trailing \"double\"\n\ +or \"single\" argument. These are the only valid classes.\n\ +@seealso{randn, rande, randg, randp}\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + retval = do_rand (args, nargin, "rand", "uniform"); + + return retval; +} + +// FIXME -- The old generator (selected when "seed" is set) will not +// work properly if compiled to use 64-bit integers. + +/* +%!test # "state" can be a scalar +%! rand ("state", 12); x = rand (1,4); +%! rand ("state", 12); y = rand (1,4); +%! assert (x, y); +%!test # "state" can be a vector +%! rand ("state", [12,13]); x = rand (1,4); +%! rand ("state", [12;13]); y = rand (1,4); +%! assert (x, y); +%!test # querying "state" doesn't disturb sequence +%! rand ("state", 12); rand (1,2); x = rand (1,2); +%! rand ("state", 12); rand (1,2); +%! s = rand ("state"); y = rand (1,2); +%! assert (x, y); +%! rand ("state", s); z = rand (1,2); +%! assert (x, z); +%!test # "seed" must be a scalar +%! rand ("seed", 12); x = rand (1,4); +%! rand ("seed", 12); y = rand (1,4); +%! assert (x, y); +%!error <seed must be a real scalar> rand ("seed", [12,13]) +%!test # querying "seed" returns a value which can be used later +%! s = rand ("seed"); x = rand (1,2); +%! rand ("seed", s); y = rand (1,2); +%! assert (x, y); +%!test # querying "seed" doesn't disturb sequence +%! rand ("seed", 12); rand (1,2); x = rand (1,2); +%! rand ("seed", 12); rand (1,2); +%! s = rand ("seed"); y = rand (1,2); +%! assert (x, y); +%! rand ("seed", s); z = rand (1,2); +%! assert (x, z); +*/ + +/* +%!test +%! # Test fixed state +%! rand ("state", 1); +%! assert (rand (1,6), [0.1343642441124013 0.8474337369372327 0.763774618976614 0.2550690257394218 0.495435087091941 0.4494910647887382], 1e-6); +%!test +%! # Test fixed seed +%! rand ("seed", 1); +%! assert (rand (1,6), [0.8668024251237512 0.9126510815694928 0.09366085007786751 0.1664607301354408 0.7408077004365623 0.7615650338120759], 1e-6); +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! rand ("state", 12); +%! x = rand (100000, 1); +%! assert (max (x) < 1); #*** Please report this!!! *** +%! assert (min (x) > 0); #*** Please report this!!! *** +%! assert (mean (x), 0.5, 0.0024); +%! assert (var (x), 1/48, 0.0632); +%! assert (skewness (x), 0, 0.012); +%! assert (kurtosis (x), -6/5, 0.0094); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! rand ("seed", 12); +%! x = rand (100000, 1); +%! assert (max (x) < 1); #*** Please report this!!! *** +%! assert (min (x) > 0); #*** Please report this!!! *** +%! assert (mean (x), 0.5, 0.0024); +%! assert (var (x), 1/48, 0.0632); +%! assert (skewness (x), 0, 0.012); +%! assert (kurtosis (x), -6/5, 0.0094); +%! endif +*/ + +static std::string current_distribution = octave_rand::distribution (); + +DEFUN (randn, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} randn (@var{n})\n\ +@deftypefnx {Built-in Function} {} randn (@var{n}, @var{m}, @dots{})\n\ +@deftypefnx {Built-in Function} {} randn ([@var{n} @var{m} @dots{}])\n\ +@deftypefnx {Built-in Function} {@var{v} =} randn (\"state\")\n\ +@deftypefnx {Built-in Function} {} randn (\"state\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randn (\"state\", \"reset\")\n\ +@deftypefnx {Built-in Function} {@var{v} =} randn (\"seed\")\n\ +@deftypefnx {Built-in Function} {} randn (\"seed\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randn (\"seed\", \"reset\")\n\ +@deftypefnx {Built-in Function} {} randn (@dots{}, \"single\")\n\ +@deftypefnx {Built-in Function} {} randn (@dots{}, \"double\")\n\ +Return a matrix with normally distributed random\n\ +elements having zero mean and variance one. The arguments are\n\ +handled the same as the arguments for @code{rand}.\n\ +\n\ +By default, @code{randn} uses the Marsaglia and Tsang ``Ziggurat technique''\n\ +to transform from a uniform to a normal distribution.\n\ +\n\ +The class of the value returned can be controlled by a trailing \"double\"\n\ +or \"single\" argument. These are the only valid classes.\n\ +\n\ +Reference: G. Marsaglia and W.W. Tsang,\n\ +@cite{Ziggurat Method for Generating Random Variables},\n\ +J. Statistical Software, vol 5, 2000,\n\ +@url{http://www.jstatsoft.org/v05/i08/})\n\ +\n\ +@seealso{rand, rande, randg, randp}\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + retval = do_rand (args, nargin, "randn", "normal"); + + return retval; +} + +/* +%!test +%! # Test fixed state +%! randn ("state", 1); +%! assert (randn (1, 6), [-2.666521678978671 -0.7381719971724564 1.507903992673601 0.6019427189162239 -0.450661261143348 -0.7054431351574116], 1e-6); +%!test +%! # Test fixed seed +%! randn ("seed", 1); +%! assert (randn (1, 6), [-1.039402365684509 -1.25938892364502 0.1968704611063004 0.3874166905879974 -0.5976632833480835 -0.6615074276924133], 1e-6); +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randn ("state", 12); +%! x = randn (100000, 1); +%! assert (mean (x), 0, 0.01); +%! assert (var (x), 1, 0.02); +%! assert (skewness (x), 0, 0.02); +%! assert (kurtosis (x), 0, 0.04); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randn ("seed", 12); +%! x = randn (100000, 1); +%! assert (mean (x), 0, 0.01); +%! assert (var (x), 1, 0.02); +%! assert (skewness (x), 0, 0.02); +%! assert (kurtosis (x), 0, 0.04); +%! endif +*/ + +DEFUN (rande, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} rande (@var{n})\n\ +@deftypefnx {Built-in Function} {} rande (@var{n}, @var{m}, @dots{})\n\ +@deftypefnx {Built-in Function} {} rande ([@var{n} @var{m} @dots{}])\n\ +@deftypefnx {Built-in Function} {@var{v} =} rande (\"state\")\n\ +@deftypefnx {Built-in Function} {} rande (\"state\", @var{v})\n\ +@deftypefnx {Built-in Function} {} rande (\"state\", \"reset\")\n\ +@deftypefnx {Built-in Function} {@var{v} =} rande (\"seed\")\n\ +@deftypefnx {Built-in Function} {} rande (\"seed\", @var{v})\n\ +@deftypefnx {Built-in Function} {} rande (\"seed\", \"reset\")\n\ +@deftypefnx {Built-in Function} {} rande (@dots{}, \"single\")\n\ +@deftypefnx {Built-in Function} {} rande (@dots{}, \"double\")\n\ +Return a matrix with exponentially distributed random elements. The\n\ +arguments are handled the same as the arguments for @code{rand}.\n\ +\n\ +By default, @code{randn} uses the Marsaglia and Tsang ``Ziggurat technique''\n\ +to transform from a uniform to an exponential distribution.\n\ +\n\ +The class of the value returned can be controlled by a trailing \"double\"\n\ +or \"single\" argument. These are the only valid classes.\n\ +\n\ +Reference: G. Marsaglia and W.W. Tsang,\n\ +@cite{Ziggurat Method for Generating Random Variables},\n\ +J. Statistical Software, vol 5, 2000,\n\ +@url{http://www.jstatsoft.org/v05/i08/})\n\ +\n\ +@seealso{rand, randn, randg, randp}\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + retval = do_rand (args, nargin, "rande", "exponential"); + + return retval; +} + +/* +%!test +%! # Test fixed state +%! rande ("state", 1); +%! assert (rande (1, 6), [3.602973885835625 0.1386190677555021 0.6743112889616958 0.4512830847258422 0.7255744741233175 0.3415969205292291], 1e-6); +%!test +%! # Test fixed seed +%! rande ("seed", 1); +%! assert (rande (1, 6), [0.06492075175653866 1.717980206012726 0.4816154008731246 0.5231300676241517 0.103910739364359 1.668931916356087], 1e-6); +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally +%! rande ("state", 1); +%! x = rande (100000, 1); +%! assert (min (x) > 0); # *** Please report this!!! *** +%! assert (mean (x), 1, 0.01); +%! assert (var (x), 1, 0.03); +%! assert (skewness (x), 2, 0.06); +%! assert (kurtosis (x), 6, 0.7); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally +%! rande ("seed", 1); +%! x = rande (100000, 1); +%! assert (min (x)>0); # *** Please report this!!! *** +%! assert (mean (x), 1, 0.01); +%! assert (var (x), 1, 0.03); +%! assert (skewness (x), 2, 0.06); +%! assert (kurtosis (x), 6, 0.7); +%! endif +*/ + +DEFUN (randg, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} randg (@var{n})\n\ +@deftypefnx {Built-in Function} {} randg (@var{n}, @var{m}, @dots{})\n\ +@deftypefnx {Built-in Function} {} randg ([@var{n} @var{m} @dots{}])\n\ +@deftypefnx {Built-in Function} {@var{v} =} randg (\"state\")\n\ +@deftypefnx {Built-in Function} {} randg (\"state\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randg (\"state\", \"reset\")\n\ +@deftypefnx {Built-in Function} {@var{v} =} randg (\"seed\")\n\ +@deftypefnx {Built-in Function} {} randg (\"seed\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randg (\"seed\", \"reset\")\n\ +@deftypefnx {Built-in Function} {} randg (@dots{}, \"single\")\n\ +@deftypefnx {Built-in Function} {} randg (@dots{}, \"double\")\n\ +Return a matrix with @code{gamma (@var{a},1)} distributed random elements.\n\ +The arguments are handled the same as the arguments for @code{rand},\n\ +except for the argument @var{a}.\n\ +\n\ +This can be used to generate many distributions:\n\ +\n\ +@table @asis\n\ +@item @code{gamma (a, b)} for @code{a > -1}, @code{b > 0}\n\ +\n\ +@example\n\ +r = b * randg (a)\n\ +@end example\n\ +\n\ +@item @code{beta (a, b)} for @code{a > -1}, @code{b > -1}\n\ +\n\ +@example\n\ +@group\n\ +r1 = randg (a, 1)\n\ +r = r1 / (r1 + randg (b, 1))\n\ +@end group\n\ +@end example\n\ +\n\ +@item @code{Erlang (a, n)}\n\ +\n\ +@example\n\ +r = a * randg (n)\n\ +@end example\n\ +\n\ +@item @code{chisq (df)} for @code{df > 0}\n\ +\n\ +@example\n\ +r = 2 * randg (df / 2)\n\ +@end example\n\ +\n\ +@item @code{t (df)} for @code{0 < df < inf} (use randn if df is infinite)\n\ +\n\ +@example\n\ +r = randn () / sqrt (2 * randg (df / 2) / df)\n\ +@end example\n\ +\n\ +@item @code{F (n1, n2)} for @code{0 < n1}, @code{0 < n2}\n\ +\n\ +@example\n\ +@group\n\ +## r1 equals 1 if n1 is infinite\n\ +r1 = 2 * randg (n1 / 2) / n1\n\ +## r2 equals 1 if n2 is infinite\n\ +r2 = 2 * randg (n2 / 2) / n2\n\ +r = r1 / r2\n\n\ +@end group\n\ +@end example\n\ +\n\ +@item negative @code{binomial (n, p)} for @code{n > 0}, @code{0 < p <= 1}\n\ +\n\ +@example\n\ +r = randp ((1 - p) / p * randg (n))\n\ +@end example\n\ +\n\ +@item non-central @code{chisq (df, L)}, for @code{df >= 0} and @code{L > 0}\n\ +(use chisq if @code{L = 0})\n\ +\n\ +@example\n\ +@group\n\ +r = randp (L / 2)\n\ +r(r > 0) = 2 * randg (r(r > 0))\n\ +r(df > 0) += 2 * randg (df(df > 0)/2)\n\ +@end group\n\ +@end example\n\ +\n\ +@item @code{Dirichlet (a1, @dots{} ak)}\n\ +\n\ +@example\n\ +@group\n\ +r = (randg (a1), @dots{}, randg (ak))\n\ +r = r / sum (r)\n\ +@end group\n\ +@end example\n\ +\n\ +@end table\n\ +\n\ +The class of the value returned can be controlled by a trailing \"double\"\n\ +or \"single\" argument. These are the only valid classes.\n\ +@seealso{rand, randn, rande, randp}\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + if (nargin < 1) + error ("randg: insufficient arguments"); + else + retval = do_rand (args, nargin, "randg", "gamma", true); + + return retval; +} + +/* +%!test +%! randg ("state", 12) +%! assert (randg ([-inf, -1, 0, inf, nan]), [nan, nan, nan, nan, nan]); # *** Please report + +%!test +%! # Test fixed state +%! randg ("state", 1); +%! assert (randg (0.1, 1, 6), [0.0103951513331241 8.335671459898252e-05 0.00138691397249762 0.000587308416993855 0.495590518784736 2.3921917414795e-12], 1e-6); +%!test +%! # Test fixed state +%! randg ("state", 1); +%! assert (randg (0.95, 1, 6), [3.099382433255327 0.3974529788871218 0.644367450750855 1.143261091802246 1.964111762696822 0.04011915547957939], 1e-6); +%!test +%! # Test fixed state +%! randg ("state", 1); +%! assert (randg (1, 1, 6), [0.2273389379645993 1.288822625058359 0.2406335209340746 1.218869553370733 1.024649860162554 0.09631230343599533], 1e-6); +%!test +%! # Test fixed state +%! randg ("state", 1); +%! assert (randg (10, 1, 6), [3.520369644331133 15.15369864472106 8.332112081991205 8.406211067432674 11.81193475187611 10.88792728177059], 1e-5); +%!test +%! # Test fixed state +%! randg ("state", 1); +%! assert (randg (100, 1, 6), [75.34570255262264 115.4911985594699 95.23493031356388 95.48926019250911 106.2397448229803 103.4813150404118], 1e-4); +%!test +%! # Test fixed seed +%! randg ("seed", 1); +%! assert (randg (0.1, 1, 6), [0.07144210487604141 0.460641473531723 0.4749028384685516 0.06823389977216721 0.000293838675133884 1.802567535340305e-12], 1e-6); +%!test +%! # Test fixed seed +%! randg ("seed", 1); +%! assert (randg (0.95, 1, 6), [1.664905071258545 1.879976987838745 1.905677795410156 0.9948706030845642 0.5606933236122131 0.0766092911362648], 1e-6); +%!test +%! # Test fixed seed +%! randg ("seed", 1); +%! assert (randg (1, 1, 6), [0.03512085229158401 0.6488978862762451 0.8114678859710693 0.1666885763406754 1.60791552066803 1.90356981754303], 1e-6); +%!test +%! # Test fixed seed +%! randg ("seed", 1); +%! assert (randg (10, 1, 6), [6.566435813903809 10.11648464202881 10.73162078857422 7.747178077697754 6.278522491455078 6.240195751190186], 1e-5); +%!test +%! # Test fixed seed +%! randg ("seed", 1); +%! assert (randg (100, 1, 6), [89.40208435058594 101.4734725952148 103.4020004272461 93.62763214111328 88.33104705810547 88.1871337890625], 1e-4); + +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("state", 12); +%! a = 0.1; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.01); +%! assert (skewness (x), 2/sqrt (a), 1); +%! assert (kurtosis (x), 6/a, 50); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("state", 12); +%! a = 0.95; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.04); +%! assert (skewness (x), 2/sqrt (a), 0.2); +%! assert (kurtosis (x), 6/a, 2); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("state", 12); +%! a = 1; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.04); +%! assert (skewness (x), 2/sqrt (a), 0.2); +%! assert (kurtosis (x), 6/a, 2); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("state", 12); +%! a = 10; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.1); +%! assert (var (x), a, 0.5); +%! assert (skewness (x), 2/sqrt (a), 0.1); +%! assert (kurtosis (x), 6/a, 0.5); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("state", 12); +%! a = 100; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.2); +%! assert (var (x), a, 2); +%! assert (skewness (x), 2/sqrt (a), 0.05); +%! assert (kurtosis (x), 6/a, 0.2); +%! endif +%!test +%! randg ("seed", 12); +%!assert (randg ([-inf, -1, 0, inf, nan]), [nan, nan, nan, nan, nan]) # *** Please report +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("seed", 12); +%! a = 0.1; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.01); +%! assert (skewness (x), 2/sqrt (a), 1); +%! assert (kurtosis (x), 6/a, 50); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("seed", 12); +%! a = 0.95; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.04); +%! assert (skewness (x), 2/sqrt (a), 0.2); +%! assert (kurtosis (x), 6/a, 2); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("seed", 12); +%! a = 1; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.01); +%! assert (var (x), a, 0.04); +%! assert (skewness (x), 2/sqrt (a), 0.2); +%! assert (kurtosis (x), 6/a, 2); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("seed", 12); +%! a = 10; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.1); +%! assert (var (x), a, 0.5); +%! assert (skewness (x), 2/sqrt (a), 0.1); +%! assert (kurtosis (x), 6/a, 0.5); +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randg ("seed", 12); +%! a = 100; +%! x = randg (a, 100000, 1); +%! assert (mean (x), a, 0.2); +%! assert (var (x), a, 2); +%! assert (skewness (x), 2/sqrt (a), 0.05); +%! assert (kurtosis (x), 6/a, 0.2); +%! endif +*/ + +DEFUN (randp, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} randp (@var{l}, @var{n})\n\ +@deftypefnx {Built-in Function} {} randp (@var{l}, @var{n}, @var{m}, @dots{})\n\ +@deftypefnx {Built-in Function} {} randp (@var{l}, [@var{n} @var{m} @dots{}])\n\ +@deftypefnx {Built-in Function} {@var{v} =} randp (\"state\")\n\ +@deftypefnx {Built-in Function} {} randp (\"state\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randp (\"state\", \"reset\")\n\ +@deftypefnx {Built-in Function} {@var{v} =} randp (\"seed\")\n\ +@deftypefnx {Built-in Function} {} randp (\"seed\", @var{v})\n\ +@deftypefnx {Built-in Function} {} randp (\"seed\", \"reset\")\n\ +@deftypefnx {Built-in Function} {} randp (@dots{}, \"single\")\n\ +@deftypefnx {Built-in Function} {} randp (@dots{}, \"double\")\n\ +Return a matrix with Poisson distributed random elements with mean value\n\ +parameter given by the first argument, @var{l}. The arguments\n\ +are handled the same as the arguments for @code{rand}, except for the\n\ +argument @var{l}.\n\ +\n\ +Five different algorithms are used depending on the range of @var{l}\n\ +and whether or not @var{l} is a scalar or a matrix.\n\ +\n\ +@table @asis\n\ +@item For scalar @var{l} @leq{} 12, use direct method.\n\ +W.H. Press, et al., @cite{Numerical Recipes in C},\n\ +Cambridge University Press, 1992.\n\ +\n\ +@item For scalar @var{l} > 12, use rejection method.[1]\n\ +W.H. Press, et al., @cite{Numerical Recipes in C},\n\ +Cambridge University Press, 1992.\n\ +\n\ +@item For matrix @var{l} @leq{} 10, use inversion method.[2]\n\ +E. Stadlober, et al., WinRand source code, available via FTP.\n\ +\n\ +@item For matrix @var{l} > 10, use patchwork rejection method.\n\ +E. Stadlober, et al., WinRand source code, available via FTP, or\n\ +H. Zechner, @cite{Efficient sampling from continuous and discrete\n\ +unimodal distributions}, Doctoral Dissertation, 156pp., Technical\n\ +University Graz, Austria, 1994.\n\ +\n\ +@item For @var{l} > 1e8, use normal approximation.\n\ +L. Montanet, et al., @cite{Review of Particle Properties}, Physical Review\n\ +D 50 p1284, 1994.\n\ +@end table\n\ +\n\ +The class of the value returned can be controlled by a trailing \"double\"\n\ +or \"single\" argument. These are the only valid classes.\n\ +@seealso{rand, randn, rande, randg}\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + if (nargin < 1) + error ("randp: insufficient arguments"); + else + retval = do_rand (args, nargin, "randp", "poisson", true); + + return retval; +} + +/* +%!test +%! randp ("state", 12); +%! assert (randp ([-inf, -1, 0, inf, nan]), [nan, nan, 0, nan, nan]); # *** Please report +%!test +%! # Test fixed state +%! randp ("state", 1); +%! assert (randp (5, 1, 6), [5 5 3 7 7 3]) +%!test +%! # Test fixed state +%! randp ("state", 1); +%! assert (randp (15, 1, 6), [13 15 8 18 18 15]) +%!test +%! # Test fixed state +%! randp ("state", 1); +%! assert (randp (1e9, 1, 6), [999915677 999976657 1000047684 1000019035 999985749 999977692], -1e-6) +%!test +%! # Test fixed state +%! randp ("seed", 1); +%! %%assert (randp (5, 1, 6), [8 2 3 6 6 8]) +%! assert (randp (5, 1, 5), [8 2 3 6 6]) +%!test +%! # Test fixed state +%! randp ("seed", 1); +%! assert (randp (15, 1, 6), [15 16 12 10 10 12]) +%!test +%! # Test fixed state +%! randp ("seed", 1); +%! assert (randp (1e9, 1, 6), [1000006208 1000012224 999981120 999963520 999963072 999981440], -1e-6) +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randp ("state", 12); +%! for a = [5, 15, 1e9; 0.03, 0.03, -5e-3; 0.03, 0.03, 0.03] +%! x = randp (a (1), 100000, 1); +%! assert (min (x) >= 0); # *** Please report this!!! *** +%! assert (mean (x), a(1), a(2)); +%! assert (var (x), a(1), 0.02*a(1)); +%! assert (skewness (x), 1/sqrt (a(1)), a(3)); +%! assert (kurtosis (x), 1/a(1), 3*a(3)); +%! endfor +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randp ("state", 12); +%! for a = [5, 15, 1e9; 0.03, 0.03, -5e-3; 0.03, 0.03, 0.03] +%! x = randp (a(1)*ones (100000, 1), 100000, 1); +%! assert (min (x) >= 0); # *** Please report this!!! *** +%! assert (mean (x), a(1), a(2)); +%! assert (var (x), a(1), 0.02*a(1)); +%! assert (skewness (x), 1/sqrt (a(1)), a(3)); +%! assert (kurtosis (x), 1/a(1), 3*a(3)); +%! endfor +%! endif +%!test +%! randp ("seed", 12); +%! assert (randp ([-inf, -1, 0, inf, nan]), [nan, nan, 0, nan, nan]); # *** Please report +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randp ("seed", 12); +%! for a = [5, 15, 1e9; 0.03, 0.03, -5e-3; 0.03, 0.03, 0.03] +%! x = randp (a(1), 100000, 1); +%! assert (min (x) >= 0); # *** Please report this!!! *** +%! assert (mean (x), a(1), a(2)); +%! assert (var (x), a(1), 0.02*a(1)); +%! assert (skewness (x), 1/sqrt (a(1)), a(3)); +%! assert (kurtosis (x), 1/a(1), 3*a(3)); +%! endfor +%! endif +%!test +%! if (__random_statistical_tests__) +%! # statistical tests may fail occasionally. +%! randp ("seed", 12); +%! for a = [5, 15, 1e9; 0.03, 0.03, -5e-3; 0.03, 0.03, 0.03] +%! x = randp (a(1)*ones (100000, 1), 100000, 1); +%! assert (min (x) >= 0); # *** Please report this!!! *** +%! assert (mean (x), a(1), a(2)); +%! assert (var (x), a(1), 0.02*a(1)); +%! assert (skewness (x), 1/sqrt (a(1)), a(3)); +%! assert (kurtosis (x), 1/a(1), 3*a(3)); +%! endfor +%! endif +*/ + +DEFUN (randperm, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} randperm (@var{n})\n\ +@deftypefnx {Built-in Function} {} randperm (@var{n}, @var{m})\n\ +Return a row vector containing a random permutation of @code{1:@var{n}}.\n\ +If @var{m} is supplied, return @var{m} unique entries, sampled without\n\ +replacement from @code{1:@var{n}}. The complexity is O(@var{n}) in\n\ +memory and O(@var{m}) in time, unless @var{m} < @var{n}/5, in which case\n\ +O(@var{m}) memory is used as well. The randomization is performed using\n\ +rand(). All permutations are equally likely.\n\ +@seealso{perms}\n\ +@end deftypefn") +{ + +#ifdef USE_UNORDERED_MAP_WITH_TR1 +using std::tr1::unordered_map; +#else +using std::unordered_map; +#endif + + int nargin = args.length (); + octave_value retval; + + if (nargin == 1 || nargin == 2) + { + octave_idx_type n, m; + + n = args(0).idx_type_value (true); + + if (nargin == 2) + m = args(1).idx_type_value (true); + else + m = n; + + if (m < 0 || n < 0) + error ("randperm: M and N must be non-negative"); + + if (m > n) + error ("randperm: M must be less than or equal to N"); + + // Quick and dirty heuristic to decide if we allocate or not the + // whole vector for tracking the truncated shuffle. + bool short_shuffle = m < n/5 && m < 1e5; + + if (! error_state) + { + // Generate random numbers. + NDArray r = octave_rand::nd_array (dim_vector (1, m)); + double *rvec = r.fortran_vec (); + + octave_idx_type idx_len = short_shuffle ? m : n; + Array<octave_idx_type> idx (dim_vector (1, idx_len)); + octave_idx_type *ivec = idx.fortran_vec (); + + for (octave_idx_type i = 0; i < idx_len; i++) + ivec[i] = i; + + if (short_shuffle) + { + unordered_map<octave_idx_type, octave_idx_type> map (m); + + // Perform the Knuth shuffle only keeping track of moved + // entries in the map + for (octave_idx_type i = 0; i < m; i++) + { + octave_idx_type k = i + + gnulib::floor (rvec[i] * (n - i)); + + //For shuffling first m entries, no need to use extra + //storage + if (k < m) + { + std::swap (ivec[i], ivec[k]); + } + else + { + if (map.find (k) == map.end ()) + map[k] = k; + + std::swap (ivec[i], map[k]); + } + } + } + else + { + + // Perform the Knuth shuffle of the first m entries + for (octave_idx_type i = 0; i < m; i++) + { + octave_idx_type k = i + + gnulib::floor (rvec[i] * (n - i)); + std::swap (ivec[i], ivec[k]); + } + } + + // Convert to doubles, reusing r. + for (octave_idx_type i = 0; i < m; i++) + rvec[i] = ivec[i] + 1; + + if (m < n) + idx.resize (dim_vector (1, m)); + + // Now create an array object with a cached idx_vector. + retval = new octave_matrix (r, idx_vector (idx)); + } + } + else + print_usage (); + + return retval; +} + +/* +%!assert (sort (randperm (20)), 1:20) +%!assert (length (randperm (20,10)), 10) + +%!test +%! rand ("seed", 0); +%! for i = 1:100 +%! p = randperm (305, 30); +%! assert (length (unique (p)), 30); +%! endfor +*/