Class: Ignis::Random::Generator
- Inherits:
-
Object
- Object
- Ignis::Random::Generator
- Defined in:
- lib/nvruby/random/generator.rb
Overview
GPU random number generator using cuRAND
Constant Summary collapse
- GENERATOR_TYPES =
Available generator types
{ default: CuRANDBindings::CURAND_RNG_PSEUDO_DEFAULT, xorwow: CuRANDBindings::CURAND_RNG_PSEUDO_XORWOW, mrg32k3a: CuRANDBindings::CURAND_RNG_PSEUDO_MRG32K3A, mtgp32: CuRANDBindings::CURAND_RNG_PSEUDO_MTGP32, mt19937: CuRANDBindings::CURAND_RNG_PSEUDO_MT19937, philox: CuRANDBindings::CURAND_RNG_PSEUDO_PHILOX4_32_10, sobol32: CuRANDBindings::CURAND_RNG_QUASI_SOBOL32, scrambled_sobol32: CuRANDBindings::CURAND_RNG_QUASI_SCRAMBLED_SOBOL32, sobol64: CuRANDBindings::CURAND_RNG_QUASI_SOBOL64, scrambled_sobol64: CuRANDBindings::CURAND_RNG_QUASI_SCRAMBLED_SOBOL64 }.freeze
Instance Attribute Summary collapse
-
#device_index ⇒ Integer
readonly
Device index.
-
#generator_type ⇒ Symbol
readonly
Generator type.
-
#seed ⇒ Integer?
readonly
Seed value.
Class Method Summary collapse
-
.release_finalizer(handle) ⇒ Proc
Create a finalizer for generator cleanup.
-
.version ⇒ Integer
Get cuRAND version.
Instance Method Summary collapse
-
#destroy! ⇒ void
Destroy the generator and free resources.
-
#destroyed? ⇒ Boolean
Check if generator has been destroyed.
-
#initialize(generator_type: :xorwow, seed: nil, device: nil) ⇒ Generator
constructor
A new instance of Generator.
-
#integers(shape) ⇒ NvArray
Generate raw 32-bit unsigned integers.
-
#log_normal(shape, mean: 0.0, std: 1.0, dtype: :float32) ⇒ NvArray
Generate log-normal random numbers.
-
#normal(shape, mean: 0.0, std: 1.0, dtype: :float32) ⇒ NvArray
Generate normal (Gaussian) random numbers.
-
#poisson(shape, lambda_param:) ⇒ NvArray
Generate Poisson-distributed random numbers.
-
#quasi_random? ⇒ Boolean
Check if quasi-random generator.
-
#set_offset(offset) ⇒ self
Set generator offset.
-
#set_seed(seed) ⇒ self
Set the random seed.
-
#set_stream(stream) ⇒ self
Set CUDA stream.
- #to_s ⇒ String
-
#uniform(shape, low: 0.0, high: 1.0, dtype: :float32) ⇒ NvArray
Generate uniform random numbers in [0, 1).
Constructor Details
#initialize(generator_type: :xorwow, seed: nil, device: nil) ⇒ Generator
Returns a new instance of Generator.
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/nvruby/random/generator.rb', line 35 def initialize(generator_type: :xorwow, seed: nil, device: nil) CuRANDBindings.ensure_loaded! @generator_type = generator_type @seed = seed @device_index = device || Ignis.configuration.default_device @destroyed = false @handle = create_generator set_seed(seed) if seed ObjectSpace.define_finalizer(self, self.class.release_finalizer(@handle)) end |
Instance Attribute Details
#device_index ⇒ Integer (readonly)
Returns Device index.
30 31 32 |
# File 'lib/nvruby/random/generator.rb', line 30 def device_index @device_index end |
#generator_type ⇒ Symbol (readonly)
Returns Generator type.
24 25 26 |
# File 'lib/nvruby/random/generator.rb', line 24 def generator_type @generator_type end |
#seed ⇒ Integer? (readonly)
Returns Seed value.
27 28 29 |
# File 'lib/nvruby/random/generator.rb', line 27 def seed @seed end |
Class Method Details
.release_finalizer(handle) ⇒ Proc
Create a finalizer for generator cleanup
247 248 249 250 251 252 |
# File 'lib/nvruby/random/generator.rb', line 247 def release_finalizer(handle) proc do CuRANDBindings.ensure_loaded! CuRANDBindings.curandDestroyGenerator(handle) end end |
.version ⇒ Integer
Get cuRAND version
256 257 258 259 260 261 262 |
# File 'lib/nvruby/random/generator.rb', line 256 def version CuRANDBindings.ensure_loaded! version_ptr = FFI::MemoryPointer.new(:int) status = CuRANDBindings.curandGetVersion(version_ptr) CuRANDBindings.check_status!(status, "Get cuRAND version") version_ptr.read_int end |
Instance Method Details
#destroy! ⇒ void
This method returns an undefined value.
Destroy the generator and free resources
228 229 230 231 232 233 234 235 |
# File 'lib/nvruby/random/generator.rb', line 228 def destroy! return if @destroyed CuRANDBindings.curandDestroyGenerator(@handle) @handle = nil @destroyed = true ObjectSpace.undefine_finalizer(self) end |
#destroyed? ⇒ Boolean
Check if generator has been destroyed
222 223 224 |
# File 'lib/nvruby/random/generator.rb', line 222 def destroyed? @destroyed end |
#integers(shape) ⇒ NvArray
Generate raw 32-bit unsigned integers
202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/nvruby/random/generator.rb', line 202 def integers(shape) raise InvalidOperationError, "Generator has been destroyed" if @destroyed output = NvArray.new(shape: shape, dtype: :uint32, device: @device_index) output.to_device status = CuRANDBindings.curandGenerate(@handle, output.device_ffi_ptr, output.size) CuRANDBindings.check_status!(status, "Generate integers") output end |
#log_normal(shape, mean: 0.0, std: 1.0, dtype: :float32) ⇒ NvArray
Generate log-normal random numbers
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/nvruby/random/generator.rb', line 156 def log_normal(shape, mean: 0.0, std: 1.0, dtype: :float32) raise InvalidOperationError, "Generator has been destroyed" if @destroyed raise UnsupportedDTypeError.new(dtype, operation: "log_normal") unless %i[float32 float64].include?(dtype) size = Array(shape).reduce(1, :*) padded_size = size.even? ? size : size + 1 temp_output = CUDA::Memory.new(padded_size * DType.byte_size(dtype), device: @device_index) status = if dtype == :float32 CuRANDBindings.curandGenerateLogNormal(@handle, temp_output.ffi_ptr, padded_size, mean, std) else CuRANDBindings.curandGenerateLogNormalDouble(@handle, temp_output.ffi_ptr, padded_size, mean, std) end CuRANDBindings.check_status!(status, "Generate log-normal") output = NvArray.new(shape: shape, dtype: dtype, device: @device_index) output.to_device output.device_memory.copy_from_device(temp_output, count: output.nbytes) temp_output.free! output end |
#normal(shape, mean: 0.0, std: 1.0, dtype: :float32) ⇒ NvArray
Generate normal (Gaussian) random numbers
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 148 |
# File 'lib/nvruby/random/generator.rb', line 122 def normal(shape, mean: 0.0, std: 1.0, dtype: :float32) raise InvalidOperationError, "Generator has been destroyed" if @destroyed raise UnsupportedDTypeError.new(dtype, operation: "normal") unless %i[float32 float64].include?(dtype) # cuRAND requires even number of elements for normal distribution size = Array(shape).reduce(1, :*) padded_size = size.even? ? size : size + 1 temp_output = CUDA::Memory.new(padded_size * DType.byte_size(dtype), device: @device_index) status = if dtype == :float32 CuRANDBindings.curandGenerateNormal(@handle, temp_output.ffi_ptr, padded_size, mean, std) else CuRANDBindings.curandGenerateNormalDouble(@handle, temp_output.ffi_ptr, padded_size, mean, std) end CuRANDBindings.check_status!(status, "Generate normal") # Create output array and copy output = NvArray.new(shape: shape, dtype: dtype, device: @device_index) output.to_device output.device_memory.copy_from_device(temp_output, count: output.nbytes) temp_output.free! output end |
#poisson(shape, lambda_param:) ⇒ NvArray
Generate Poisson-distributed random numbers
186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/nvruby/random/generator.rb', line 186 def poisson(shape, lambda_param:) raise InvalidOperationError, "Generator has been destroyed" if @destroyed raise ArgumentError, "Lambda must be positive" unless lambda_param.positive? output = NvArray.new(shape: shape, dtype: :uint32, device: @device_index) output.to_device status = CuRANDBindings.curandGeneratePoisson(@handle, output.device_ffi_ptr, output.size, lambda_param) CuRANDBindings.check_status!(status, "Generate Poisson") output end |
#quasi_random? ⇒ Boolean
Check if quasi-random generator
216 217 218 |
# File 'lib/nvruby/random/generator.rb', line 216 def quasi_random? %i[sobol32 scrambled_sobol32 sobol64 scrambled_sobol64].include?(@generator_type) end |
#set_offset(offset) ⇒ self
Set generator offset
66 67 68 69 70 71 72 73 |
# File 'lib/nvruby/random/generator.rb', line 66 def set_offset(offset) raise InvalidOperationError, "Generator has been destroyed" if @destroyed status = CuRANDBindings.curandSetGeneratorOffset(@handle, offset) CuRANDBindings.check_status!(status, "Set generator offset") self end |
#set_seed(seed) ⇒ self
Set the random seed
52 53 54 55 56 57 58 59 60 61 |
# File 'lib/nvruby/random/generator.rb', line 52 def set_seed(seed) raise InvalidOperationError, "Generator has been destroyed" if @destroyed raise ArgumentError, "Cannot set seed for quasi-random generator" if quasi_random? @seed = seed status = CuRANDBindings.curandSetPseudoRandomGeneratorSeed(@handle, seed) CuRANDBindings.check_status!(status, "Set generator seed") self end |
#set_stream(stream) ⇒ self
Set CUDA stream
78 79 80 81 82 83 84 85 |
# File 'lib/nvruby/random/generator.rb', line 78 def set_stream(stream) raise InvalidOperationError, "Generator has been destroyed" if @destroyed status = CuRANDBindings.curandSetStream(@handle, stream.handle) CuRANDBindings.check_status!(status, "Set generator stream") self end |
#to_s ⇒ String
238 239 240 241 |
# File 'lib/nvruby/random/generator.rb', line 238 def to_s status = @destroyed ? "destroyed" : "active" "Generator(type=#{@generator_type}, seed=#{@seed || 'auto'}, #{status})" end |
#uniform(shape, low: 0.0, high: 1.0, dtype: :float32) ⇒ NvArray
Generate uniform random numbers in [0, 1)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/nvruby/random/generator.rb', line 93 def uniform(shape, low: 0.0, high: 1.0, dtype: :float32) raise InvalidOperationError, "Generator has been destroyed" if @destroyed raise UnsupportedDTypeError.new(dtype, operation: "uniform") unless %i[float32 float64].include?(dtype) output = NvArray.new(shape: shape, dtype: dtype, device: @device_index) output.to_device count = output.size status = if dtype == :float32 CuRANDBindings.curandGenerateUniform(@handle, output.device_ffi_ptr, count) else CuRANDBindings.curandGenerateUniformDouble(@handle, output.device_ffi_ptr, count) end CuRANDBindings.check_status!(status, "Generate uniform") # Scale to [low, high) if needed scale_uniform(output, low, high) unless low.zero? && high == 1.0 output end |