Crate sobol_burley[][src]

Expand description

A seedable Owen-scrambled Sobol sequence.

This crate is based on the paper Practical Hash-based Owen Scrambling by Brent Burley, but with an improved hash from Building a Better LK Hash and more dimensions due to Kuo et al.

This crate is geared towards practical graphics applications, and as such has some limitations:

  • The maximum sequence length is 2^16.
  • The maximum number of dimensions is 256 (although this can be worked around with seeding).
  • Only f32 output is supported.

These are all trade-offs for the sake of better performance and a smaller memory footprint.

Basic usage

Basic usage is pretty straightforward:

use sobol_burley::sample;

// Print 1024 3-dimensional points.
for i in 0..1024 {
    let x = sample(i, 0, 0);
    let y = sample(i, 1, 0);
    let z = sample(i, 2, 0);
    println!("({}, {}, {})", x, y, z);

The first parameter of sample() is the index of the sample you want, and the second parameter is the index of the dimension you want. The parameters are zero-indexed, and outputs are in the interval [0, 1).

If all you want is a single Owen-scrambled Sobol sequence, then this is all you need. You can ignore the third parameter.


(Note: the sample() function automatically uses a different Owen scramble for each dimension, so seeding is unnecessary if you just want a single Sobol sequence.)

The third parameter of sample() is a seed that produces statistically independent Sobol sequences via the scrambling+shuffling technique from Brent Burley’s paper.

One of the applications for this is to decorrelate the error between related integral estimates. For example, in a 3d renderer you might pass a different seed to each pixel so that error in the pixel colors shows up as noise instead of as structured artifacts.

Another important application is “padding” the dimensions of a Sobol sequence. By changing the seed we can re-use the same dimensions over and over to create an arbitrarily high-dimensional sequence. For example:

// Print 10000 dimensions of a single sample.
for dimension in 0..10000 {
    let seed = dimension / 4;
    let n = sample(0, dimension % 4, seed);
    println!("{}", n);

In this example we change seeds every 4 dimensions. This allows us to re-use the same 4 dimensions over and over, extending the sequence to as many dimensions as we like. Each set of 4 dimensions is stratified within itself, but is randomly decorrelated from the other sets.

See Burley’s paper for justification of this padding approach as well as recommendations about its use.


You can use sample_4d() to compute four dimensions at once, returned as an array of floats.

On x86-64 architectures sample_4d() utilizes SIMD for a roughly 4x speed-up. On other architectures it still computes correct results, but SIMD isn’t supported yet.

Importantly, sample() and sample_4d() always compute identical results:

for dimension_set in 0..10 {
    let a = [
        sample(0, dimension_set * 4, 0),
        sample(0, dimension_set * 4 + 1, 0),
        sample(0, dimension_set * 4 + 2, 0),
        sample(0, dimension_set * 4 + 3, 0)
    let b = sample_4d(0, dimension_set, 0);

    assert_eq!(a, b);

The difference is only in performance and how the dimensions are indexed.



The number of available dimensions.


The number of available 4d dimension sets.



Compute one dimension of a single sample in the Sobol sequence.


Compute four dimensions of a single sample in the Sobol sequence.