changeset 28116:7fa1d6f670f5

rng.m: More style fixes. * rng.m: Improve documentation. Use intermediate variable "arg1" in place of "varargin{1}" to improve readability of code. Use parentheses around switch statement's argument. Indent "otherwise" to same level as "case" statements. Prefix error() messages with "rng:". Use strcmp, rather than ismember, for performance. Save and restore random number generator state in BIST tests so it is not affected by tests. Update BIST validation tests to occur in the order that they are encountered in the file. * matrix.txi: Move DOCSTRING for rng to be near documentation of "state" and "seed". * stats.txi: Delete DOCSTRING for rng here.
author Rik <rik@octave.org>
date Wed, 19 Feb 2020 10:45:31 -0800
parents 91f245908132
children 1db62b2d6c39
files doc/interpreter/matrix.txi doc/interpreter/stats.txi scripts/general/rng.m
diffstat 3 files changed, 155 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/matrix.txi	Wed Feb 19 12:45:09 2020 -0500
+++ b/doc/interpreter/matrix.txi	Wed Feb 19 10:45:31 2020 -0800
@@ -175,6 +175,8 @@
 
 @DOCSTRING(randg)
 
+@DOCSTRING(rng)
+
 The generators operate in the new or old style together, it is not
 possible to mix the two.  Initializing any generator with
 @qcode{"state"} or @qcode{"seed"} causes the others to switch to the
--- a/doc/interpreter/stats.txi	Wed Feb 19 12:45:09 2020 -0500
+++ b/doc/interpreter/stats.txi	Wed Feb 19 10:45:31 2020 -0800
@@ -284,5 +284,3 @@
 @DOCSTRING(discrete_rnd)
 
 @DOCSTRING(empirical_rnd)
-
-@DOCSTRING(rng)
--- a/scripts/general/rng.m	Wed Feb 19 12:45:09 2020 -0500
+++ b/scripts/general/rng.m	Wed Feb 19 10:45:31 2020 -0800
@@ -25,38 +25,40 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {} {} rng (@var{seed})
-## @deftypefnx {} {} rng (@var{seed}, @var{generator})
+## @deftypefnx {} {} rng (@var{seed}, "@var{generator}")
 ## @deftypefnx {} {} rng ("shuffle")
-## @deftypefnx {} {} rng ("shuffle", @var{generator})
+## @deftypefnx {} {} rng ("shuffle", "@var{generator}")
 ## @deftypefnx {} {} rng ("default")
 ## @deftypefnx {} {@var{s} =} rng ()
 ## @deftypefnx {} {} rng (@var{s})
 ## @deftypefnx {} {@var{s} =} rng (@dots{})
-## Set or query the seed of the random number generator used by rand and randn.
+## Set or query the seed of the random number generator used by @code{rand} and
+## @code{randn}.
 ##
-## Input scalar numeric value @qcode{seed} is used to initialize the state
+## The input @code{seed} is a scalar numeric value used to initialize the state
 ## vector of the random number generator.
 ##
 ## The optional string @var{generator} specifies the type of random number
-## generator to be used. Its value can be @qcode{"twister"}, @qcode{"v5uniform"}
-## or @qcode{"v5normal"}. The @qcode{"twister"} keyword is described below.
-## @qcode{"v5uniform"} and @qcode{"v5normal"} refer to older versions of Octave
-## that used to use a different random number generator.
+## generator to be used.  Its value can be @qcode{"twister"},
+## @qcode{"v5uniform"}, or @qcode{"v5normal"}.  The @qcode{"twister"} keyword
+## is described below.  @qcode{"v5uniform"} and @qcode{"v5normal"} refer to
+## older versions of Octave that used to use a different random number
+## generator.
 ##
-## The state or seed of the random number generator can be reset to a new random
-## value using the @qcode{"shuffle"} keyword.
+## The state or seed of the random number generator can be reset to a new
+## random value using the @qcode{"shuffle"} keyword.
 ##
-## The state or seed of the random number generator can be reset to its default
-## values using the @qcode{"default"} keyword. The default values are to use the
-## Mersenne Twister with a seed of 0.
+## The random number generator can be reset to default values using the
+## @qcode{"default"} keyword.  The default values are to use the Mersenne
+## Twister generator with a seed of 0.
 ##
 ## The optional return value @var{s} contains the state of the random number
-## generator at the time the function is called (i.e. before it might be
-## modified according to the input arguments). It is encoded as a structure
-## variable with three fields: @qcode{"Type"}, @qcode{"Seed"} and
-## @qcode{"State"}.
-## Later, the random number generator can be restored to the state @var{s} using
-## rng (s).
+## generator at the time the function is called (i.e., before it might be
+## modified according to the input arguments).  It is encoded as a structure
+## variable with three fields: @qcode{"Type"}, @qcode{"Seed"}, and
+## @qcode{"State"}.  The random number generator can be restored to the state
+## @var{s} using @code{rng (@var{s})}.  This is useful when the identical
+## sequence of pseudo-random numbers is required for an algorithm. 
 ##
 ## By default, and with the @qcode{"twister"} option, pseudo-random sequences
 ## are computed using the Mersenne Twister with a period of @math{2^{19937}-1}
@@ -100,32 +102,31 @@
     return;
   endif
 
-  if (isscalar (varargin{1}) && isnumeric (varargin{1})
-      && isreal (varargin{1}) && varargin{1} >= 0)
-    s_rand = s_randn = varargin{1};
+  arg1 = varargin{1};
+  if (isscalar (arg1) && isnumeric (arg1) && isreal (arg1) && arg1 >= 0)
+    s_rand = s_randn = arg1;
     generator = check_generator (varargin(2:end));
 
-  elseif (ischar (varargin{1}) && strcmpi (varargin{1}, "shuffle"))
+  elseif (ischar (arg1) && strcmpi (arg1, "shuffle"))
     ## Seed the random number generator based on the current time
-    s_rand = s_randn = "reset"; # or sum (1000*clock)
+    s_rand = s_randn = "reset";  # or sum (1000*clock)
     generator = check_generator (varargin(2:end));
 
-  elseif (ischar (varargin{1}) && strcmpi (varargin{1}, "default")
-          && nargin == 1)
+  elseif (ischar (arg1) && strcmpi (arg1, "default") && nargin == 1)
     generator = "twister";
-    s_rand = s_randn = 0; # In Matlab, seed 0 corresponds to 5489
+    s_rand = s_randn = 0;  # FIXME: In Matlab, seed 0 corresponds to 5489
 
-  elseif (isstruct (varargin{1}) && isscalar (varargin{1}) && nargin == 1)
-    if (numfields (varargin{1}) != 3 || ! isfield (varargin{1}, "Type")
-        || ! isfield (varargin{1}, "Seed") || ! isfield (varargin{1}, "State"))
-      error ("input structure not compatible with the one returned by rng ()");
+  elseif (isstruct (arg1) && isscalar (arg1) && nargin == 1)
+    if (numfields (arg1) != 3
+        || ! all (isfield (arg1, {"Type", "Seed", "State"})))
+      error ('rng: input structure requires "Type", "Seed", "State" fields"');
     endif
     ## Only the internal state "State" and generator type "Type" are needed
-    generator = varargin{1}.Type;
-    if (iscell (varargin{1}.State))
-      [s_rand, s_randn] = deal (varargin{1}.State{:});
+    generator = arg1.Type;
+    if (iscell (arg1.State))
+      [s_rand, s_randn] = deal (arg1.State{:});
     else
-      s_rand = s_randn = varargin{1}.State;
+      s_rand = s_randn = arg1.State;
     endif
 
   else
@@ -136,7 +137,7 @@
   if (isempty (generator))
     generator = s.Type;
   endif
-  switch generator
+  switch (generator)
     case "twister"
       rand ("state", s_rand);
       randn ("state", s_randn);
@@ -151,8 +152,8 @@
     case "v5normal"
       randn ("seed", s_randn);
 
-  otherwise
-    error ("unknown type of random number generator");
+    otherwise
+      error ('rng: invalid GENERATOR: "%s"', generator);
 
   endswitch
 
@@ -168,106 +169,135 @@
     gen = "";
     return;
   elseif (! iscellstr (val))
-    error ("second input must be a type of random number generator");
+    error ("rng: GENERATOR must be a string");
   endif
   gen = tolower (char (val));
-  if (ismember (gen, {"simdtwister", "combrecursive", "philox", "threefry", "multfibonacci", "v4"}))
-    error ("random number generator '%s' is not available in Octave", gen);
-  elseif (! ismember (gen, {"twister", "v5uniform", "v5normal"}))
-    error ("unknown random number generator '%s'", gen);
+  if (any (strcmp (gen, {"simdtwister", "combrecursive", "philox", "threefry", "multfibonacci", "v4"})))
+    error ('rng: random number generator "%s" is not available in Octave', gen);
+  elseif (! any (strcmp (gen, {"twister", "v5uniform", "v5normal"})))
+    error ('rng: unknown random number generator "%s"', gen);
   endif
 endfunction
 
 
 %!test
-%! rng (42);
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! rng (42);
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
-%! s1 = rng ();
-%! s2 = rng (42);
-%! assert (isequal (s1, s2));
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! s3 = rng (42);
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
+%! state = rng ();
+%! unwind_protect
+%!   rng (42);
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   rng (42);
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%!   s1 = rng ();
+%!   s2 = rng (42);
+%!   assert (isequal (s1, s2));
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   s3 = rng (42);
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%! unwind_protect_cleanup
+%!   rng (state);
+%! end_unwind_protect
+
+%!test
+%! state = rng ();
+%! unwind_protect
+%!   rng (42, "twister");
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   rng (42, "twister");
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%!   s1 = rng ();
+%!   s2 = rng (42, "twister");
+%!   assert (isequal (s1, s2));
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   s3 = rng (42, "twister");
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%! unwind_protect_cleanup
+%!   rng (state);
+%! end_unwind_protect
 
 %!test
-%! rng (42, "twister");
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! rng (42, "twister");
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
-%! s1 = rng ();
-%! s2 = rng (42, "twister");
-%! assert (isequal (s1, s2));
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! s3 = rng (42, "twister");
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
-
-%!test
-%! rng ("shuffle");
-%! rng ("shuffle", "twister");
-%! s = rng ("shuffle");
-%! assert (! isequal (s, rng ("shuffle")));
-%! s = rng ("shuffle", "twister");
-%! assert (! isequal (s, rng ("shuffle", "twister")));
+%! state = rng ();
+%! unwind_protect
+%!   rng ("shuffle");
+%!   rng ("shuffle", "twister");
+%!   s = rng ("shuffle");
+%!   assert (! isequal (s, rng ("shuffle")));
+%!   s = rng ("shuffle", "twister");
+%!   assert (! isequal (s, rng ("shuffle", "twister")));
+%! unwind_protect_cleanup
+%!   rng (state);
+%! end_unwind_protect
 
 %!test
-%! rng ("default");
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! rng (0, "twister");
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
-%! rng (0, "twister");
-%! s = rng ("default");
-%! assert (isequal (s, rng ()));
+%! state = rng ();
+%! unwind_protect
+%!   rng ("default");
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   rng (0, "twister");
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%!   rng (0, "twister");
+%!   s = rng ("default");
+%!   assert (isequal (s, rng ()));
+%! unwind_protect_cleanup
+%!   rng (state);
+%! end_unwind_protect
 
 %!test
-%! s = rng ();
-%! ru1 = rand ();
-%! rn1 = randn ();
-%! rng (s);
-%! ru2 = rand ();
-%! rn2 = randn ();
-%! assert (ru2, ru1);
-%! assert (rn2, rn1);
-%! rng (42);  rand (1,2);  x = rand (1,2);
-%! rng (42);  rand (1,2);  s = rng ();  y = rand (1,2);
-%! assert (x, y);
-%! rng (s);  z = rand (1,2);
-%! assert (x, z);
-%! s1 = rng ();
-%! s2 = rng (s1);
-%! assert (isequal (s1, s2));
+%! state = rng ();
+%! unwind_protect
+%!   s = rng ();
+%!   ru1 = rand ();
+%!   rn1 = randn ();
+%!   rng (s);
+%!   ru2 = rand ();
+%!   rn2 = randn ();
+%!   assert (ru2, ru1);
+%!   assert (rn2, rn1);
+%!   rng (42);  rand (1,2);  x = rand (1,2);
+%!   rng (42);  rand (1,2);  s = rng ();  y = rand (1,2);
+%!   assert (x, y);
+%!   rng (s);  z = rand (1,2);
+%!   assert (x, z);
+%!   s1 = rng ();
+%!   s2 = rng (s1);
+%!   assert (isequal (s1, s2));
+%! unwind_protect_cleanup
+%!   rng (state);
+%! end_unwind_protect
 
 ## Test input validation
 %!error <Invalid call> rng (1, 2, 3)
+%!error <Invalid call> rng (eye (2))
 %!error <Invalid call> rng ({})
-%!error <Invalid call> rng ("unknown")
-%!error <Invalid call> rng (eye (2))
-%!error <Invalid call> rng (struct ("Seed", {1 ,2}))
-%!error <Invalid call> rng (-1)
-%!error <Invalid call> rng (struct ("Type",[],"State",[],"Seed",[]), 2)
+%!error <Invalid call> rng (2i)
+%!error <Invalid call> rng (-2)
+%!error <Invalid call> rng ("foobar")
 %!error <Invalid call> rng ("default", "twister")
-%!error <second input must be a type of random number generator> rng (0, struct ())
-%!error <unknown random number generator> rng (0, "unknown")
-%!error <second input must be a type of random number generator> rng ("shuffle", struct ())
-%!error <unknown random number generator> rng ("shuffle", "unknown")
+%!error <Invalid call> rng (struct ("Seed", {1, 2}))
+%!error <Invalid call> rng (struct ("Type",[],"State",[],"Seed",[]), "twister")
+%!error <input structure requires "Type"> rng (struct ())
+%!error <input structure requires "Type">
+%! rng (struct ("Type1",[],"State",[],"Seed",[]));
+%!error <GENERATOR must be a string> rng (0, struct ())
+%!error <"philox" is not available in Octave> rng (0, "philox")
+%!error <GENERATOR must be a string> rng ("shuffle", struct ())
+%!error <unknown random number generator "foobar"> rng ("shuffle", "foobar")