Class: Xoroshiro::Random

Inherits:
Object
  • Object
show all
Defined in:
lib/xoroshiro.rb,
ext/xoroshiro/xoroshiro.c

Overview

Provides pseudo-random variate generation based on Xoroshiro256**.

Quoting the original Xoroshiro creators:

…xoshiro256** 1.0 [is] one of our all-purpose, rock-solid generators. It has excellent (sub-ns) speed, a state (256 bits) that is large enough for any parallel application, and it passes all tests we are aware of.

This generator outperforms MT19937, the underlying engine for Kernel::rand, in both speed and statistical performance.

Instance Method Summary collapse

Constructor Details

#initialize(range = nil, seed: nil) ⇒ Random

Arguments

@param range [nil, Range, Numeric] specify the range and type of the values produced by :each. (default: nil)

  • nil -> generate Uniform(0,1) Float values

  • Range -> generate uniformly distributed values within the range of type:

    • if both bounds are Integer -> Integer

    • otherwise -> Float

  • Numeric value -> evaluated as Range 0..value

@param seed: [nil, Numeric] initialize the generator state for reproducibility. (default: nil)

  • nil -> use system entropy via SecureRandom to generate 256 bits of initial state.

  • Numeric value -> calculate SHA256 hash of value to use as the initial state.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/xoroshiro.rb', line 63

def initialize(range = nil, seed: nil)
  set_state(Xoroshiro.gen_seed_state(seed))
  gen_method = method(:each_u_range)
  case range
  when nil
    gen_method = method(:each_u)
  when Numeric
    @range = 0..range
  when Range
    @range = range
  else
    warn "Invalid range argument: #{range}"
    gen_method = method(:each_u)
  end
  @generator = gen_method.call
end

Instance Method Details

#eachObject

Yields random values determined by the range argument of initialize.

Returns:

  • an Enumerator.



83
84
85
# File 'lib/xoroshiro.rb', line 83

def each
  @generator
end

#get_stateObject

Retrieve the current 256 bit state of the generator. Complement to set_state.

Returns:

  • an array of four integers.



46
47
48
49
50
51
52
53
54
# File 'ext/xoroshiro/xoroshiro.c', line 46

static VALUE get_state(VALUE self) {
  seed_struct* seed_values;
  Data_Get_Struct(self, seed_struct, seed_values);
  VALUE state = rb_ary_new();
  for (uint32_t i = 0; i < seed_values->size; ++i) {
    rb_ary_push(state, ULL2NUM(seed_values->seed[i]));
  }
  return state;
}

#jumpObject

Update the state to the equivalent of 2^128 calls to rand(). This can be used to generate 2^128 non-overlapping subsequences for parallel computations.



61
62
63
64
65
66
# File 'ext/xoroshiro/xoroshiro.c', line 61

static VALUE do_jump(VALUE self) {
  seed_struct* seed_values;
  Data_Get_Struct(self, seed_struct, seed_values);
  jump(seed_values);
  return self;
}

#long_jumpObject

Update the state to the equivalent of 2^192 calls to rand(). This can be used to generate 2^64 non-overlapping subsequences for parallel computations.



73
74
75
76
77
78
# File 'ext/xoroshiro/xoroshiro.c', line 73

static VALUE do_long_jump(VALUE self) {
  seed_struct* seed_values;
  Data_Get_Struct(self, seed_struct, seed_values);
  long_jump(seed_values);
  return self;
}

#rand(*args) ⇒ Object

Generate a random value as specified by args.

Arguments

@param args [nil, Range, Numeric] specify the range and type of the generated value. (default: nil)

  • nil -> generate a Uniform(0,1) Float value

  • Range -> generate a uniformly distributed value within the range of type:

    • if both bounds are Integer -> Integer

    • otherwise -> Float

  • Numeric value -> evaluated as Range (0..value)

Returns:

  • a single value consistent with the specified range and type.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'ext/xoroshiro/xoroshiro.c', line 95

static VALUE rand_method(int argc, VALUE* argv, VALUE self) {
  seed_struct* seed_values;
  Data_Get_Struct(self, seed_struct, seed_values);

  VALUE argument;
  rb_scan_args(argc, argv, "01", &argument);

  VALUE outcome = Qnil;

  if (rb_obj_is_kind_of(argument, rb_cRange)) {
    VALUE rb_beg;
    VALUE rb_end;
    int excl;
    if (!rb_range_values(argument, &rb_beg, &rb_end, &excl)) return Qnil;
    if (NIL_P(rb_beg) || NIL_P(rb_end)) return Qnil;
    if (rb_obj_is_kind_of(rb_beg, rb_cNumeric) && rb_obj_is_kind_of(rb_end, rb_cNumeric)) {
      if (RB_FLOAT_TYPE_P(rb_beg) || RB_FLOAT_TYPE_P(rb_end)) {
        double begin = NUM2DBL(rb_beg);
        double drange = NUM2DBL(rb_end) - begin;
        outcome = DBL2NUM(begin + next_double(seed_values) * drange);
      } else {
        int64_t begin = NUM2LL(rb_beg);
        int64_t end = NUM2LL(rb_end);
        int64_t irange = end - begin;
        if (!excl) {
          irange += 1;
        }
        if (irange >= 0) outcome = LL2NUM(begin + nearlydivisionless(irange, seed_values));
      }
    }
    return outcome;
  } else {
    switch (TYPE(argument)) {
      case T_NIL:
        outcome = DBL2NUM(next_double(seed_values));
        break;
      case T_FLOAT:
        outcome = DBL2NUM(next_double(seed_values) * NUM2DBL(argument));
        break;
      case T_FIXNUM: {
        int64_t upper_lim = NUM2LL(argument);
        if (upper_lim > 0) outcome = ULL2NUM(nearlydivisionless(upper_lim, seed_values));
        break;
      }
      case T_BIGNUM: {
        uint64_t upper_lim = NUM2ULL(argument);
        outcome = ULL2NUM(nearlydivisionless(upper_lim, seed_values));
        break;
      }
    }
    return outcome;
  }
}

#set_state(seeds) ⇒ Object

Explicitly set the 256 bit state of the generator. Complement to get_state.

Arguments

@param seeds [Array of 4 Integers]

Returns:

  • self



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'ext/xoroshiro/xoroshiro.c', line 23

static VALUE set_state(VALUE self, VALUE seeds) {
  Check_Type(seeds, T_ARRAY);
  long seedsize = RARRAY_LEN(seeds);
  if (seedsize != 4) {
    rb_raise(rb_eTypeError, "set_state: Four integer seed values required");
  }

  seed_struct* seed_values;
  Data_Get_Struct(self, seed_struct, seed_values);

  seed_values->size = (uint32_t) seedsize;
  seed_values->seed = (uint64_t *) malloc(seed_values->size * sizeof(uint64_t));
  for (uint32_t i = 0; i < seedsize; ++i) {
    seed_values->seed[i] = NUM2ULL(rb_ary_entry(seeds, i));
  }
  return self;
}