ruRAND User Guide
Compute libraries · Runtime API · 中文 | 日本語 | Deutsch | Русский
ruRAND generates device tensors with uniform, normal, and Bernoulli distributions. Use rurand::tensor to allocate new tensors, or the same-named functions at the crate root to fill existing device storage.
1. Configure dependencies
The Cargo package is ruRAND; its Rust import name is rurand. Feature tensor enables device tensor interfaces. This configuration places the application directory alongside the RUDA source directory. See Getting started for NVIDIA setup.
[dependencies]
rurand = { package = "ruRAND", path = "../RUDA/ruRAND", default-features = false, features = ["std", "tensor"] }
ruda-core = { path = "../RUDA/ruda-core", default-features = false, features = ["std", "tensor-host-data"] }
ruda-kernel = { path = "../RUDA/ruda-kernel", default-features = false, features = ["frontend-std", "device-tensor"] }
ruda-driver-cuda = { path = "../RUDA/ruda-driver-cuda", default-features = false, features = ["std"] }
2. Generate random tensors
This complete src/main.rs generates F32 tensors of shape [2, 3] and replays a call by resetting the seed. Run cargo run from the application directory:
use ruda_core::tensor::DType;
use ruda_driver_cuda::{CudaDevice, CudaRuntime};
use ruda_kernel::tensor::readback::into_data_sync;
use rurand::tensor::{
random_bernoulli, random_like_uniform, random_normal, random_uniform,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let device = CudaDevice::default();
rurand::seed(42);
let uniform = random_uniform::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
let first = into_data_sync(uniform.clone()).to_vec::<f32>()?;
rurand::seed(42);
let repeated = random_uniform::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
let repeated = into_data_sync(repeated).to_vec::<f32>()?;
assert_eq!(first, repeated);
assert!(first.iter().all(|&x| (0.0..1.0).contains(&x)));
let normal = random_normal::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
let mask = random_bernoulli::<CudaRuntime>([2, 3].into(), &device, 0.25, DType::F32);
let noise = random_like_uniform(&uniform, -0.1, 0.1, DType::F32);
let mask = into_data_sync(mask).to_vec::<f32>()?;
assert!(mask.iter().all(|&x| x == 0.0 || x == 1.0));
println!("uniform={first:?}");
println!("normal={:?}", into_data_sync(normal).to_vec::<f32>()?);
println!("mask={mask:?}");
println!("noise={:?}", into_data_sync(noise).to_vec::<f32>()?);
Ok(())
}
random_like_uniform takes only shape and device from the reference. It allocates a new tensor without changing or copying the reference values. Its final argument explicitly selects output dtype.
random_bernoulli returns zeros and ones in the selected dtype, not an automatically created Boolean mask. This example returns F32 values 0.0 and 1.0.
3. Parameters and distributions
The first three tensor functions share shape: Shape, device: &R::Device, and a final dtype: DType parameter. The returned RudaTensor<R> has the specified shape, dtype, and device.
| Function and parameter order | Distribution parameters |
|---|---|
random_uniform(shape, device, lower_bound, upper_bound, dtype) |
f32 lower and upper bounds |
random_normal(shape, device, mean, std, dtype) |
f32 mean and standard deviation; std is not variance |
random_bernoulli(shape, device, probability, dtype) |
f32 probability of returning 1 |
random_like_uniform(&reference, lower_bound, upper_bound, dtype) |
Reference tensor, f32 bounds, output dtype |
Supply finite parameters consistent with the distribution: lower bound below upper bound, nonnegative normal standard deviation, and Bernoulli probability in [0, 1]. These interfaces do not validate these ranges through Result; do not use invalid parameters to request an error return.
Uniform generation first produces F32 u ∈ [0, 1), computes lower_bound + (upper_bound - lower_bound) × u, then casts to the output dtype. The F32 [0, 1) case excludes 1. Scaling to other intervals or converting to lower precision can round a value near the upper bound to that bound.
Normal generation uses an FP32 Box–Muller transform before casting to the output dtype. Choosing F64 storage does not increase internal random floating-point precision. Bernoulli compares u < probability, so probability 0 produces all zeros and probability 1 produces all ones.
4. Seeds and call order
rurand::seed(seed: u64) resets shared host random state. Each generation call draws new seeds from that state and advances it:
- Set the seed once to initialize a sequence, then generate successive batches.
- To replay a sequence, reset the same seed and preserve call order, shape, dtype, and device configuration.
- Do not reset the same seed before every training batch unless repeated random inputs are intended.
- Concurrent threads share this state; interleaving affects which seeds each call receives. It is not a separate generator object per thread or device.
The replay comparison above uses one process, one device, and matching arguments. Matching seeds alone do not imply elementwise equality with another random library or backend.
5. Fill an existing buffer
Use crate-root launch functions to reuse an output allocation. This function takes an already allocated F32 device tensor, writes random values into its storage, and returns it:
use ruda_kernel::{
dsl::{Runtime, prelude::LaunchError},
tensor::RudaTensor,
};
fn fill_uniform<R: Runtime>(
output: RudaTensor<R>,
lower: f32,
upper: f32,
) -> Result<RudaTensor<R>, LaunchError> {
rurand::random_uniform(
&output.client,
lower,
upper,
output.clone().binding(),
output.dtype.into(),
)?;
Ok(output)
}
The function does not invoke a tensor allocator. Other handles sharing that storage also observe the writes. Corresponding functions for normal and Bernoulli values are rurand::random_normal(&client, mean, std, binding, dtype) and rurand::random_bernoulli(&client, probability, binding, dtype).
Tensor-level functions panic if launching returns an error; the underlying entry point above returns Result<(), LaunchError>. Successful submission does not mean GPU execution has finished. Read the tensor back or await client synchronization. into_data_sync panics on readback failure; use asynchronous into_data(tensor).await to propagate readback errors.
API reference: Tensor interfaces, Seed state, Distributions.