From 095d858cede9eeddbf7da1d0b12391cbbf99adaa Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 11 Nov 2024 15:40:41 +0100 Subject: [PATCH] Replace ACG by Random123 (#3190) * Fix tests --- .sanitizers/undefined.supp | 1 - docs/hoc/programming/math/random.rst | 27 +- docs/python/programming/math/random.rst | 27 +- share/lib/helpdict | 1 - src/gnu/ACG.cpp | 292 ---------------------- src/gnu/ACG.h | 65 ----- src/gnu/CMakeLists.txt | 2 +- src/gnu/NrnRandom123RNG.cpp | 9 + src/gnu/NrnRandom123RNG.hpp | 6 - src/gnu/RNG.h | 2 +- src/gnu/Rand.cpp | 6 +- src/gnu/Rand.hpp | 2 +- src/ivoc/ivocrand.cpp | 36 +-- test/pytest_coreneuron/test_vector_api.py | 8 +- 14 files changed, 22 insertions(+), 462 deletions(-) delete mode 100755 src/gnu/ACG.cpp delete mode 100755 src/gnu/ACG.h diff --git a/.sanitizers/undefined.supp b/.sanitizers/undefined.supp index e8af6013cd..6b7373768b 100644 --- a/.sanitizers/undefined.supp +++ b/.sanitizers/undefined.supp @@ -25,7 +25,6 @@ pointer-overflow:coreneuron::net_receive_NetStim(coreneuron::Point_process*, int pointer-overflow:coreneuron::_net_receive__VecStim(coreneuron::Point_process*, int, double) pointer-overflow:NonLinImpRep::current(int, Memb_list*, int) pointer-overflow:pr_realcell(PreSyn&, NrnThread&, _IO_FILE*) -shift-base:ACG::asLong() shift-base:nrnRan4int unsigned-integer-overflow:_philox4x32bumpkey(r123array2x32) unsigned-integer-overflow:coreneuron::nrnran123_deletestream(coreneuron::nrnran123_State*, bool) diff --git a/docs/hoc/programming/math/random.rst b/docs/hoc/programming/math/random.rst index b037f4fcc2..78d8d11c02 100644 --- a/docs/hoc/programming/math/random.rst +++ b/docs/hoc/programming/math/random.rst @@ -34,7 +34,7 @@ Random Class from the gnu c++ class library. As of version 5.2, a cryptographic quality RNG class wrapper for :hoc:func:`mcell_ran4` was added and is available with the :hoc:meth:`Random.MCellRan4` method. The current default random generator - is :hoc:meth:`Random.ACG`. + is :hoc:meth:`Random.Random123`. As of version 7.3, a more versatile cryptographic quality generator, Random123, is available with the :hoc:meth:`Random.Random123` method. This generator @@ -71,31 +71,6 @@ Random Class -.. hoc:method:: Random.ACG - - - Syntax: - ``r.ACG()`` - - ``r.ACG(seed)`` - - ``r.ACG(seed, size)`` - - - Description: - Use a variant of the Linear Congruential Generator (algorithm M) - described in Knuth, Art of Computer Programming, Vol. III in - combination with a Fibonacci Additive Congruential Generator. This is - a "very high quality" random number generator, Default size is 55, - giving a size of 1244 bytes to the structure. Minimum size is 7 (total - 100 bytes), maximum size is 98 (total 2440 bytes). - - - ----- - - - .. hoc:method:: Random.MCellRan4 diff --git a/docs/python/programming/math/random.rst b/docs/python/programming/math/random.rst index a699677a31..e8aff9a3bc 100755 --- a/docs/python/programming/math/random.rst +++ b/docs/python/programming/math/random.rst @@ -33,7 +33,7 @@ Random Class from the gnu c++ class library. As of version 5.2, a cryptographic quality RNG class wrapper for :func:`mcell_ran4` was added and is available with the :meth:`Random.MCellRan4` method. The current default random generator - is :meth:`Random.ACG`. + is :meth:`Random.Random123`. As of version 7.3, a more versatile cryptographic quality generator, Random123, is available with the :meth:`Random.Random123` method. This generator @@ -74,31 +74,6 @@ Random Class -.. method:: Random.ACG - - - Syntax: - ``r.ACG()`` - - ``r.ACG(seed)`` - - ``r.ACG(seed, size)`` - - - Description: - Use a variant of the Linear Congruential Generator (algorithm M) - described in Knuth, Art of Computer Programming, Vol. III in - combination with a Fibonacci Additive Congruential Generator. This is - a "very high quality" random number generator, Default size is 55, - giving a size of 1244 bytes to the structure. Minimum size is 7 (total - 100 bytes), maximum size is 98 (total 2440 bytes). - - - ----- - - - .. method:: Random.MCellRan4 diff --git a/share/lib/helpdict b/share/lib/helpdict index 0db82dc69b..7d04c6a0d9 100755 --- a/share/lib/helpdict +++ b/share/lib/helpdict @@ -4,7 +4,6 @@ AbsoluteTolerance VariableStepControl Tools NEURONMainMenu GUI Reference 418 neu accept_action List classes general neuron.exe Reference 69 neuron/general/classes/list.html#accept_action accept_action SectionBrowser classes neuron neuron.exe Reference 316 neuron/neuron/classes/secbrows.html#accept_action access CurrentlyAccessedSection Section neuron neuron.exe Reference 380 neuron/neuron/secspec.html#access -ACG Random classes general neuron.exe Reference 97 neuron/general/classes/random.html#ACG action MechanismStandard classes neuron neuron.exe Reference 268 neuron/neuron/classes/mechstan.html#action action MechanismType classes neuron neuron.exe Reference 274 neuron/neuron/classes/mechtype.html#action action Shape classes neuron neuron.exe Reference 325 neuron/neuron/classes/shape.html#action diff --git a/src/gnu/ACG.cpp b/src/gnu/ACG.cpp deleted file mode 100755 index 0f9136b347..0000000000 --- a/src/gnu/ACG.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - -This file is part of the GNU C++ Library. This library is free -software; you can redistribute it and/or modify it under the terms of -the GNU Library General Public License as published by the Free -Software Foundation; either version 2 of the License, or (at your -option) any later version. This library 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 Library General Public License for more details. -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include -#ifdef __GNUG__ -#pragma implementation -#endif -#include "ACG.h" -#include - -// -// This is an extension of the older implementation of Algorithm M -// which I previously supplied. The main difference between this -// version and the old code are: -// -// + Andres searched high & low for good constants for -// the LCG. -// -// + theres more bit chopping going on. -// -// The following contains his comments. -// -// agn@UNH.CS.CMU.EDU sez.. -// -// The generator below is based on 2 well known -// methods: Linear Congruential (LCGs) and Additive -// Congruential generators (ACGs). -// -// The LCG produces the longest possible sequence -// of 32 bit random numbers, each being unique in -// that sequence (it has only 32 bits of state). -// It suffers from 2 problems: a) Independence -// isnt great, that is the (n+1)th number is -// somewhat related to the preceding one, unlike -// flipping a coin where knowing the past outcomes -// dont help to predict the next result. b) -// Taking parts of a LCG generated number can be -// quite non-random: for example, looking at only -// the least significant byte gives a permuted -// 8-bit counter (that has a period length of only -// 256). The advantage of an LCA is that it is -// perfectly uniform when run for the entire period -// length (and very uniform for smaller sequences -// too, if the parameters are chosen carefully). -// -// ACGs have extremly long period lengths and -// provide good independence. Unfortunately, -// uniformity isnt not too great. Furthermore, I -// didnt find any theoretically analysis of ACGs -// that addresses uniformity. -// -// The RNG given below will return numbers -// generated by an LCA that are permuted under -// control of a ACG. 2 permutations take place: the -// 4 bytes of one LCG generated number are -// subjected to one of 16 permutations selected by -// 4 bits of the ACG. The permutation a such that -// byte of the result may come from each byte of -// the LCG number. This effectively destroys the -// structure within a word. Finally, the sequence -// of such numbers is permuted within a range of -// 256 numbers. This greatly improves independence. -// -// -// Algorithm M as describes in Knuths "Art of Computer Programming", -// Vol 2. 1969 -// is used with a linear congruential generator (to get a good uniform -// distribution) that is permuted with a Fibonacci additive congruential -// generator to get good independence. -// -// Bit, byte, and word distributions were extensively tested and pass -// Chi-squared test near perfect scores (>7E8 numbers tested, Uniformity -// assumption holds with probability > 0.999) -// -// Run-up tests for on 7E8 numbers confirm independence with -// probability > 0.97. -// -// Plotting random points in 2d reveals no apparent structure. -// -// Autocorrelation on sequences of 5E5 numbers (A(i) = SUM X(n)*X(n-i), -// i=1..512) -// results in no obvious structure (A(i) ~ const). -// -// Except for speed and memory requirements, this generator outperforms -// random() for all tests. (random() scored rather low on uniformity tests, -// while independence test differences were less dramatic). -// -// AGN would like to.. -// thanks to M.Mauldin, H.Walker, J.Saxe and M.Molloy for inspiration & help. -// -// And I would (DGC) would like to thank Donald Kunth for AGN for letting me -// use his extensions in this implementation. -// - -// -// Part of the table on page 28 of Knuth, vol II. This allows us -// to adjust the size of the table at the expense of shorter sequences. -// - -static int randomStateTable[][3] = { -{3,7,16}, {4,9, 32}, {3,10, 32}, {1,11, 32}, {1,15,64}, {3,17,128}, -{7,18,128}, {3,20,128}, {2,21, 128}, {1,22, 128}, {5,23, 128}, {3,25, 128}, -{2,29, 128}, {3,31, 128}, {13,33, 256}, {2,35, 256}, {11,36, 256}, -{14,39,256}, {3,41,256}, {9,49,256}, {3,52,256}, {24,55,256}, {7,57, 256}, -{19,58,256}, {38,89,512}, {17,95,512}, {6,97,512}, {11,98,512}, {-1,-1,-1} }; - -// -// spatial permutation table -// RANDOM_PERM_SIZE must be a power of two -// - -#define RANDOM_PERM_SIZE 64 -uint32_t randomPermutations[RANDOM_PERM_SIZE] = { -0xffffffff, 0x00000000, 0x00000000, 0x00000000, // 3210 -0x0000ffff, 0x00ff0000, 0x00000000, 0xff000000, // 2310 -0xff0000ff, 0x0000ff00, 0x00000000, 0x00ff0000, // 3120 -0x00ff00ff, 0x00000000, 0xff00ff00, 0x00000000, // 1230 - -0xffff0000, 0x000000ff, 0x00000000, 0x0000ff00, // 3201 -0x00000000, 0x00ff00ff, 0x00000000, 0xff00ff00, // 2301 -0xff000000, 0x00000000, 0x000000ff, 0x00ffff00, // 3102 -0x00000000, 0x00000000, 0x00000000, 0xffffffff, // 2103 - -0xff00ff00, 0x00000000, 0x00ff00ff, 0x00000000, // 3012 -0x0000ff00, 0x00000000, 0x00ff0000, 0xff0000ff, // 2013 -0x00000000, 0x00000000, 0xffffffff, 0x00000000, // 1032 -0x00000000, 0x0000ff00, 0xffff0000, 0x000000ff, // 1023 - -0x00000000, 0xffffffff, 0x00000000, 0x00000000, // 0321 -0x00ffff00, 0xff000000, 0x00000000, 0x000000ff, // 0213 -0x00000000, 0xff000000, 0x0000ffff, 0x00ff0000, // 0132 -0x00000000, 0xff00ff00, 0x00000000, 0x00ff00ff // 0123 -}; - -// -// SEED_TABLE_SIZE must be a power of 2 -// -#define SEED_TABLE_SIZE 32 -static uint32_t seedTable[SEED_TABLE_SIZE] = { -0xbdcc47e5, 0x54aea45d, 0xec0df859, 0xda84637b, -0xc8c6cb4f, 0x35574b01, 0x28260b7d, 0x0d07fdbf, -0x9faaeeb0, 0x613dd169, 0x5ce2d818, 0x85b9e706, -0xab2469db, 0xda02b0dc, 0x45c60d6e, 0xffe49d10, -0x7224fea3, 0xf9684fc9, 0xfc7ee074, 0x326ce92a, -0x366d13b5, 0x17aaa731, 0xeb83a675, 0x7781cb32, -0x4ec7c92d, 0x7f187521, 0x2cf346b4, 0xad13310f, -0xb89cff2b, 0x12164de1, 0xa865168d, 0x32b56cdf -}; - -// -// The LCG used to scramble the ACG -// -// -// LC-parameter selection follows recommendations in -// "Handbook of Mathematical Functions" by Abramowitz & Stegun 10th, edi. -// -// LC_A = 251^2, ~= sqrt(2^32) = 66049 -// LC_C = result of a long trial & error series = 3907864577 -// - -static const uint32_t LC_A = 66049; -static const uint32_t LC_C = 3907864577U; -static inline uint32_t LCG(uint32_t x) -{ - return( x * LC_A + LC_C ); -} - - -ACG::ACG(uint32_t seed, int size) -{ - int l; - initialSeed = seed; - // - // Determine the size of the state table - // - - for (l = 0; - randomStateTable[l][0] != -1 && randomStateTable[l][1] < size; - l++); - - if (randomStateTable[l][1] == -1) { - l--; - } - - initialTableEntry = l; - - stateSize = randomStateTable[ initialTableEntry ][ 1 ]; - auxSize = randomStateTable[ initialTableEntry ][ 2 ]; - - // - // Allocate the state table & the auxillary table in a single malloc - // - - state = new uint32_t[stateSize + auxSize]; - auxState = &state[stateSize]; - - reset(); -} - -// -// Initialize the state -// -void -ACG::reset() -{ - uint32_t u; - - if (initialSeed < SEED_TABLE_SIZE) { - u = seedTable[ initialSeed ]; - } else { - u = initialSeed ^ seedTable[ initialSeed & (SEED_TABLE_SIZE-1) ]; - } - - - j = randomStateTable[ initialTableEntry ][ 0 ] - 1; - k = randomStateTable[ initialTableEntry ][ 1 ] - 1; - - int i; - for(i = 0; i < stateSize; i++) { - state[i] = u = LCG(u); - } - - for (i = 0; i < auxSize; i++) { - auxState[i] = u = LCG(u); - } - - k = u % stateSize; - int tailBehind = (stateSize - randomStateTable[ initialTableEntry ][ 0 ]); - j = k - tailBehind; - if (j < 0) { - j += stateSize; - } - - lcgRecurr = u; - - assert(sizeof(double) == 2 * sizeof(int32_t)); -} - -ACG::~ACG() -{ - if (state) delete [] state; - state = 0; - // don't delete auxState, it's really an alias for state. -} - -// -// Returns 32 bits of random information. -// - -uint32_t -ACG::asLong() -{ - uint32_t result = state[k] + state[j]; - state[k] = result; - j = (j <= 0) ? (stateSize-1) : (j-1); - k = (k <= 0) ? (stateSize-1) : (k-1); - - short int auxIndex = (result >> 24) & (auxSize - 1); - uint32_t auxACG = auxState[auxIndex]; - auxState[auxIndex] = lcgRecurr = LCG(lcgRecurr); - - // - // 3c is a magic number. We are doing four masks here, so we - // do not want to run off the end of the permutation table. - // This insures that we have always got four entries left. - // - uint32_t *perm = & randomPermutations[result & 0x3c]; - - result = *(perm++) & auxACG; - result |= *(perm++) & ((auxACG << 24) - | ((auxACG >> 8)& 0xffffff)); - result |= *(perm++) & ((auxACG << 16) - | ((auxACG >> 16) & 0xffff)); - result |= *(perm++) & ((auxACG << 8) - | ((auxACG >> 24) & 0xff)); - - return(result); -} diff --git a/src/gnu/ACG.h b/src/gnu/ACG.h deleted file mode 100755 index e8fcd0eb5d..0000000000 --- a/src/gnu/ACG.h +++ /dev/null @@ -1,65 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of the GNU C++ Library. This library is free -software; you can redistribute it and/or modify it under the terms of -the GNU Library General Public License as published by the Free -Software Foundation; either version 2 of the License, or (at your -option) any later version. This library 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 Library General Public License for more details. -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#pragma once - -#include "RNG.h" -#include -#ifdef __GNUG__ -//#pragma interface -#endif - -// -// Additive number generator. This method is presented in Volume II -// of The Art of Computer Programming by Knuth. I've coded the algorithm -// and have added the extensions by Andres Nowatzyk of CMU to randomize -// the result of algorithm M a bit by using an LCG & a spatial -// permutation table. -// -// The version presented uses the same constants for the LCG that Andres -// uses (chosen by trial & error). The spatial permutation table is -// the same size (it's based on word size). This is for 32-bit words. -// -// The ``auxillary table'' used by the LCG table varies in size, and -// is chosen to be the the smallest power of two which is larger than -// twice the size of the state table. -// - -class ACG : public RNG { - - uint32_t initialSeed; // used to reset generator - int initialTableEntry; - - uint32_t *state; - uint32_t *auxState; - short stateSize; - short auxSize; - uint32_t lcgRecurr; - short j; - short k; - -protected: - -public: - ACG(uint32_t seed = 0, int size = 55); - virtual ~ACG(); - // - // Return a long-words word of random bits - // - virtual uint32_t asLong(); - virtual void reset(); -}; diff --git a/src/gnu/CMakeLists.txt b/src/gnu/CMakeLists.txt index 6bf0820654..c8b5f59cdb 100644 --- a/src/gnu/CMakeLists.txt +++ b/src/gnu/CMakeLists.txt @@ -1,6 +1,5 @@ add_library( nrngnu STATIC - ACG.cpp Binomial.cpp DiscUnif.cpp Erlang.cpp @@ -11,6 +10,7 @@ add_library( mcran4.cpp NegExp.cpp Normal.cpp + NrnRandom123RNG.cpp nrnran123.cpp Poisson.cpp Rand.cpp diff --git a/src/gnu/NrnRandom123RNG.cpp b/src/gnu/NrnRandom123RNG.cpp index e69de29bb2..20dacf0885 100644 --- a/src/gnu/NrnRandom123RNG.cpp +++ b/src/gnu/NrnRandom123RNG.cpp @@ -0,0 +1,9 @@ +#include "NrnRandom123RNG.hpp" + +NrnRandom123::NrnRandom123(std::uint32_t id1, std::uint32_t id2, std::uint32_t id3) { + s_ = nrnran123_newstream3(id1, id2, id3); +} + +NrnRandom123::~NrnRandom123() { + nrnran123_deletestream(s_); +} diff --git a/src/gnu/NrnRandom123RNG.hpp b/src/gnu/NrnRandom123RNG.hpp index 5e790c939c..326cc5e672 100644 --- a/src/gnu/NrnRandom123RNG.hpp +++ b/src/gnu/NrnRandom123RNG.hpp @@ -20,9 +20,3 @@ class NrnRandom123: public RNG { } nrnran123_State* s_; }; -NrnRandom123::NrnRandom123(std::uint32_t id1, std::uint32_t id2, std::uint32_t id3) { - s_ = nrnran123_newstream3(id1, id2, id3); -} -NrnRandom123::~NrnRandom123() { - nrnran123_deletestream(s_); -} diff --git a/src/gnu/RNG.h b/src/gnu/RNG.h index ab37b7f90b..56202c1cec 100755 --- a/src/gnu/RNG.h +++ b/src/gnu/RNG.h @@ -35,7 +35,7 @@ union PrivateRNGDoubleType { // used to access doubles as unsigneds }; // -// Base class for Random Number Generators. See ACG for instances. +// Base class for Random Number Generators. // class RNG { static PrivateRNGSingleType singleMantissa; // mantissa bit vector diff --git a/src/gnu/Rand.cpp b/src/gnu/Rand.cpp index a3b526de5e..09f8fe64d6 100644 --- a/src/gnu/Rand.cpp +++ b/src/gnu/Rand.cpp @@ -1,13 +1,13 @@ #include "Rand.hpp" -#include "ACG.h" +#include "NrnRandom123RNG.hpp" #include "Normal.h" Rand::Rand(unsigned long seed, int size, Object* obj) { // printf("Rand\n"); - gen = new ACG(seed, size); + gen = new NrnRandom123(seed, size); rand = new Normal(0., 1., gen); - type_ = 0; + type_ = 4; obj_ = obj; } diff --git a/src/gnu/Rand.hpp b/src/gnu/Rand.hpp index 10e4df6380..9c80382d09 100644 --- a/src/gnu/Rand.hpp +++ b/src/gnu/Rand.hpp @@ -6,7 +6,7 @@ struct Object; /* type_: - * 0: ACG + * 0: unused * 1: unused * 2: MCellRan4 * 3: unused diff --git a/src/ivoc/ivocrand.cpp b/src/ivoc/ivocrand.cpp index 957f256874..2b6fd3b5fc 100644 --- a/src/ivoc/ivocrand.cpp +++ b/src/ivoc/ivocrand.cpp @@ -16,7 +16,6 @@ #include "ocobserv.h" #include -#include #include #include #include @@ -103,33 +102,6 @@ static void r_destruct(void* r) { delete (Rand*) r; } -// Use a variant of the Linear Congruential Generator (algorithm M) -// described in Knuth, Art of Computer Programming, Vol. III in -// combination with a Fibonacci Additive Congruential Generator. -// This is a "very high quality" random number generator, -// Default size is 55, giving a size of 1244 bytes to the structure -// minimum size is 7 (total 100 bytes), maximum size is 98 (total 2440 bytes) -// syntax: -// r.ACG([seed],[size]) - -static double r_ACG(void* r) { - Rand* x = (Rand*) r; - - unsigned long seed = 0; - int size = 55; - - if (ifarg(1)) - seed = long(*getarg(1)); - if (ifarg(2)) - size = int(chkarg(2, 7, 98)); - - x->rand->generator(new ACG(seed, size)); - x->type_ = 0; - delete x->gen; - x->gen = x->rand->generator(); - return 1.; -} - static double r_MCellRan4(void* r) { Rand* x = (Rand*) r; @@ -202,11 +174,6 @@ static double r_ran123_globalindex(void* r) { static double r_sequence(void* r) { Rand* x = (Rand*) r; - if (x->type_ != 2 && x->type_ != 4) { - hoc_execerror( - "Random.seq() can only be used if the random generator was MCellRan4 or Random123", 0); - } - if (x->type_ == 4) { uint32_t seq; char which; @@ -439,8 +406,7 @@ extern "C" void nrn_random_play() { } -static Member_func r_members[] = {{"ACG", r_ACG}, - {"MCellRan4", r_MCellRan4}, +static Member_func r_members[] = {{"MCellRan4", r_MCellRan4}, {"Random123", r_nrnran123}, {"Random123_globalindex", r_ran123_globalindex}, {"seq", r_sequence}, diff --git a/test/pytest_coreneuron/test_vector_api.py b/test/pytest_coreneuron/test_vector_api.py index ff90bafb2d..faaac86636 100644 --- a/test/pytest_coreneuron/test_vector_api.py +++ b/test/pytest_coreneuron/test_vector_api.py @@ -514,10 +514,10 @@ def test_vector_api(): vrand = h.Vector((1, 2, 3)) r = h.Random() r.poisson(12) - assert vrand.cl().setrand(r).to_python() == [10.0, 16.0, 11.0] - assert vrand.cl().setrand(r, 1, 2).to_python() == [1.0, 9.0, 18.0] - assert vrand.cl().addrand(r).to_python() == [9.0, 9.0, 16.0] - assert vrand.cl().addrand(r, 0, 1).to_python() == [13.0, 16.0, 3.0] + assert vrand.cl().setrand(r).to_python() == [22.0, 16.0, 16.0] + assert vrand.cl().setrand(r, 1, 2).to_python() == [1.0, 8.0, 12.0] + assert vrand.cl().addrand(r).to_python() == [7.0, 13.0, 12.0] + assert vrand.cl().addrand(r, 0, 1).to_python() == [9.0, 16.0, 3.0] """ Misc