[random] Seeding again

View: New views
1 Messages — Rating Filter:   Alert me  

[random] Seeding again

by Steven Watanabe-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

AMDG

The attached patch allows random number generators
to be seeded with any value.  Currently linear_congruential
and a few other generators can assert for some seeds.
(For instance minstd_rand cannot be seeded with 0).

For linear_congruential I am using the algorithm described
in n2960, section 26.5.3.1/4.  For linear_feedback_shift
I use a similar idea, adding 2^(w-k) if the seed is less than
2^(w-k).  The behavior of any code that was valid before,
should be unaffected by this change.

The patch includes test and documentation updates.  The
tests pass with gcc, msvc, and como.  I think it's ready
to go now.

Okay to commit?

In Christ,
Steven Watanabe


Index: boost/random/linear_feedback_shift.hpp
===================================================================
--- boost/random/linear_feedback_shift.hpp (revision 57432)
+++ boost/random/linear_feedback_shift.hpp (working copy)
@@ -77,7 +77,12 @@
     seed(first, last);
   }
 
-  void seed(UIntType s0 = 341) { assert(s0 >= (1 << (w-k))); value = s0; }
+  void seed(UIntType s0 = 341) {
+      if(s0 < (1 << (w-k))) {
+          s0 += 1 << (w-k);
+      }
+      value = s0;
+  }
   template<class It> void seed(It& first, It last)
   {
     if(first == last)
Index: boost/random/linear_congruential.hpp
===================================================================
--- boost/random/linear_congruential.hpp (revision 57432)
+++ boost/random/linear_congruential.hpp (working copy)
@@ -54,12 +54,9 @@
   // BOOST_STATIC_ASSERT(m == 0 || c < m);
 
   explicit linear_congruential(IntType x0 = 1)
-    : _modulus(modulus), _x(_modulus ? (x0 % _modulus) : x0)
+    : _modulus(modulus)
   {
-    assert(c || x0); /* if c == 0 and x(0) == 0 then x(n) = 0 for all n */
-    // overflow check
-    // disabled because it gives spurious "divide by zero" gcc warnings
-    // assert(m == 0 || (a*(m-1)+c) % m == (c < a ? c-a+m : c-a));
+    seed(x0);
 
     // MSVC fails BOOST_STATIC_ASSERT with std::numeric_limits at class scope
 #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
@@ -77,8 +74,22 @@
   // compiler-generated copy constructor and assignment operator are fine
   void seed(IntType x0 = 1)
   {
-    assert(c || x0);
-    _x = (_modulus ? (x0 % _modulus) : x0);
+    // wrap _x if it doesn't fit in the destination
+    if(modulus == 0) {
+      _x = x0;
+    } else {
+      _x = x0 % modulus;
+    }
+    // handle negative seeds
+    if(_x < 0) {
+      _x += modulus;
+    }
+    // adjust to the correct range
+    if(increment == 0 && _x == 0) {
+      _x = 1;
+    }
+    assert(_x >= min());
+    assert(_x <= max());
   }
 
   template<class It>
@@ -86,8 +97,7 @@
   {
     if(first == last)
       throw std::invalid_argument("linear_congruential::seed");
-    IntType value = *first++;
-    _x = (_modulus ? (value % _modulus) : value);
+    seed(*first++);
   }
 
   result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return c == 0 ? 1 : 0; }
@@ -261,7 +271,7 @@
     if(sizeof(T) < sizeof(uint64_t)) {
       return (static_cast<uint64_t>(x) << 16) | 0x330e;
     } else {
-        return(static_cast<uint64_t>(x));
+      return(static_cast<uint64_t>(x));
     }
   }
   static uint64_t cnv(float x) { return(static_cast<uint64_t>(x)); }
Index: libs/random/random-generators.html
===================================================================
--- libs/random/random-generators.html (revision 57432)
+++ libs/random/random-generators.html (working copy)
@@ -495,11 +495,21 @@
 </pre>
 
   <p><strong>Effects:</strong> Constructs a <code>linear_congruential</code>
-  generator with x(0) := <code>x0</code>.</p>
+  generator, seeding it with <code>x0</code>.</p>
+
   <pre>
 void seed(IntType x0)
 </pre>
 
+
+  <p><strong>Effects:</strong> If <code>c</code> mod <code>m</code> is zero
+  and <code>x0</code> mod <code>m</code> is zero, changes the current value of
+  the generator to 1.  Otherwise, changes it to <code>x0</code> mod
+  <code>m</code>.  If <code>c</code> is zero, distinct seeds in the range
+  [1,m) will leave the generator in distinct states.  If <code>c</code>
+  is not zero, the range is [0,m).
+  </p>
+
   <p><strong>Effects:</strong> Changes the current value x(n) of the
   generator to <code>x0</code>.</p>
 
@@ -605,8 +615,13 @@
   static const result_type min_value = 1;
   static const result_type max_value = MLCG1::max_value-1;
   additive_combine();
+  additive_combine(typename MLCG1::result_type seed);
   additive_combine(typename MLCG1::result_type seed1,
                    typename MLCG2::result_type seed2);
+  void seed();
+  void seed(typename MLCG1::result_type seed);
+  void seed(typename MLCG1::result_type seed1,
+            typename MLCG2::result_type seed2);
   result_type operator()();
   bool validation(result_type x) const;
 };
@@ -645,7 +660,16 @@
 
   <p><strong>Effects:</strong> Constructs an <code>additive_combine</code>
   generator using the default constructors of the two base generators.</p>
+
   <pre>
+additive_combine(typename MLCG1::result_type seed)
+</pre>
+
+  <p><strong>Effects:</strong> Constructs an <code>additive_combine</code>
+  generator, using <code>seed</code> as the constructor argument for
+  both base generators.</p>
+
+  <pre>
 additive_combine(typename MLCG1::result_type seed1,
                  typename MLCG2::result_type seed2)
 </pre>
@@ -655,6 +679,30 @@
   constructor argument to the first and second base generator,
   respectively.</p>
 
+  <pre>
+void seed()
+</pre>
+
+  <p><strong>Effects:</strong> Seeds an <code>additive_combine</code>
+  generator using the default seeds of the two base generators.</p>
+
+  <pre>
+void seed(typename MLCG1::result_type seed)
+</pre>
+
+  <p><strong>Effects:</strong> Seeds an <code>additive_combine</code>
+  generator, using <code>seed</code> as the seed for both base
+  generators.</p>
+
+  <pre>
+void seed(typename MLCG1::result_type seed1,
+          typename MLCG2::result_type seed2)
+</pre>
+
+  <p><strong>Effects:</strong> Seeds an <code>additive_combine</code>
+  generator, using <code>seed1</code> and <code>seed2</code> as the
+  seeds to the first and second base generator, respectively.</p>
+
   <h3><a name="ecuyer1988" id="ecuyer1988">Specialization</a></h3>
 
   <p>The specialization <code>ecuyer1988</code> was suggested in the above
@@ -997,7 +1045,7 @@
 
   <h3>Description</h3>
 
-  <p>Instantiations of class template <code>lagged_fibonacci</code> model a
+  <p>Instantiations of class template <code>lagged_fibonacci_01</code> model a
   <a href="random-concepts.html#pseudo-rng">pseudo-random number
   generator</a>. It uses a lagged Fibonacci algorithm with two lags p and q,
   evaluated in floating-point arithmetic: x(i) = x(i-p) + x(i-q) (mod 1) with
@@ -1050,7 +1098,10 @@
 
   <p><strong>Effects:</strong> Constructs a <code>minstd_rand0</code>
   generator with the constructor parameter <code>value</code> and calls
-  <code>seed</code> with it.</p>
+  <code>seed</code> with it.  Distinct seeds in the range [1, 2147483647)
+  will produce generators with different states.  Other seeds will be
+  equivalent to some seed within this range.  See
+  <a href="#linear_congruential">linear_congruential</a> for details.</p>
   <pre>
 template<class Generator> void seed(Generator & gen)
 </pre>
Index: libs/random/instantiate.cpp
===================================================================
--- libs/random/instantiate.cpp (revision 57432)
+++ libs/random/instantiate.cpp (working copy)
@@ -133,53 +133,63 @@
                    boost::gamma_distribution<RealType>(1));
 }
 
-template<class URNG, class T>
-void test_seed(URNG & urng, const T & t) {
-    URNG urng2(t);
-    BOOST_CHECK(urng == urng2);
-    urng2.seed(t);
-    BOOST_CHECK(urng == urng2);
+template<class URNG, class T, class Converted>
+void test_seed_conversion(URNG & urng, const T & t, const Converted &) {
+    Converted c = static_cast<Converted>(t);
+    if(static_cast<T>(c) == t) {
+        URNG urng2(c);
+        BOOST_CHECK_MESSAGE(urng == urng2, std::string("Testing seed constructor: ") + typeid(Converted).name());
+        urng2.seed(c);
+        BOOST_CHECK(urng == urng2);
+    }
 }
 
 // rand48 uses non-standard seeding
-template<class T>
-void test_seed(boost::rand48 & urng, const T & t) {
+template<class T, class Converted>
+void test_seed_conversion(boost::rand48 & urng, const T & t, const Converted &) {
     boost::rand48 urng2(t);
     urng2.seed(t);
 }
 
 template<class URNG, class ResultType>
-void instantiate_seed(const URNG &, const ResultType &) {
+void test_seed(const URNG &, const ResultType & v) {
+    typename URNG::result_type value = static_cast<typename URNG::result_type>(v);
+
+    URNG urng(value);
+
+    // integral types
+    test_seed_conversion(urng, value, static_cast<char>(0));
+    test_seed_conversion(urng, value, static_cast<signed char>(0));
+    test_seed_conversion(urng, value, static_cast<unsigned char>(0));
+    test_seed_conversion(urng, value, static_cast<short>(0));
+    test_seed_conversion(urng, value, static_cast<unsigned short>(0));
+    test_seed_conversion(urng, value, static_cast<int>(0));
+    test_seed_conversion(urng, value, static_cast<unsigned int>(0));
+    test_seed_conversion(urng, value, static_cast<long>(0));
+    test_seed_conversion(urng, value, static_cast<unsigned long>(0));
+#if !defined(BOOST_NO_INT64_T)
+    test_seed_conversion(urng, value, static_cast<boost::int64_t>(0));
+    test_seed_conversion(urng, value, static_cast<boost::uint64_t>(0));
+#endif
+
+    // floating point types
+    test_seed_conversion(urng, value, static_cast<float>(0));
+    test_seed_conversion(urng, value, static_cast<double>(0));
+    test_seed_conversion(urng, value, static_cast<long double>(0));
+}
+
+template<class URNG, class ResultType>
+void instantiate_seed(const URNG & urng, const ResultType &) {
     {
         URNG urng;
         URNG urng2;
         urng2.seed();
         BOOST_CHECK(urng == urng2);
     }
-    {
-        int value = 127;
-        URNG urng(value);
-
-        // integral types
-        test_seed(urng, static_cast<char>(value));
-        test_seed(urng, static_cast<signed char>(value));
-        test_seed(urng, static_cast<unsigned char>(value));
-        test_seed(urng, static_cast<short>(value));
-        test_seed(urng, static_cast<unsigned short>(value));
-        test_seed(urng, static_cast<int>(value));
-        test_seed(urng, static_cast<unsigned int>(value));
-        test_seed(urng, static_cast<long>(value));
-        test_seed(urng, static_cast<unsigned long>(value));
-#if !defined(BOOST_NO_INT64_T)
-        test_seed(urng, static_cast<boost::int64_t>(value));
-        test_seed(urng, static_cast<boost::uint64_t>(value));
-#endif
-
-        // floating point types
-        test_seed(urng, static_cast<float>(value));
-        test_seed(urng, static_cast<double>(value));
-        test_seed(urng, static_cast<long double>(value));
-    }
+    test_seed(urng, 0);
+    test_seed(urng, 127);
+    test_seed(urng, 539157235);
+    test_seed(urng, ~0u);
 }
 
 template<class URNG, class ResultType>

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost