/* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of NVIDIA CORPORATION nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2009 Mike Giles, Oxford University. All rights * reserved. * Portions Copyright (c) 2008 Frances Y. Kuo and Stephen Joe. All rights * reserved. * * Sobol Quasi-random Number Generator example * * Based on CUDA code submitted by Mike Giles, Oxford University, United Kingdom * http://people.maths.ox.ac.uk/~gilesm/ * * and C code developed by Stephen Joe, University of Waikato, New Zealand * and Frances Kuo, University of New South Wales, Australia * http://web.maths.unsw.edu.au/~fkuo/sobol/ * * For theoretical background see: * * P. Bratley and B.L. Fox. * Implementing Sobol's quasirandom sequence generator * http://portal.acm.org/citation.cfm?id=42288 * ACM Trans. on Math. Software, 14(1):88-100, 1988 * * S. Joe and F. Kuo. * Remark on algorithm 659: implementing Sobol's quasirandom sequence generator. * http://portal.acm.org/citation.cfm?id=641879 * ACM Trans. on Math. Software, 29(1):49-57, 2003 */ #include #include #include #include #include "sobol.h" #include "sobol_gold.h" #include "sobol_primitives.h" #define k_2powneg32 2.3283064E-10F // Windows does not provide ffs (find first set) so here is a // fairly simple implementation. // WIN32 is defined on 32 and 64 bit Windows #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) int ffs(const unsigned int &i) { unsigned int v = i; unsigned int count; if (!v) { count = 0; } else { count = 2; if ((v & 0xffff) == 0) { v >>= 16; count += 16; } if ((v & 0xff) == 0) { v >>= 8; count += 8; } if ((v & 0xf) == 0) { v >>= 4; count += 4; } if ((v & 0x3) == 0) { v >>= 2; count += 2; } count -= v & 0x1; } return count; } #endif // Create the direction numbers, based on the primitive polynomials. void initSobolDirectionVectors(int n_dimensions, unsigned int *directions) { unsigned int *v = directions; for (int dim = 0; dim < n_dimensions; dim++) { // First dimension is a special case if (dim == 0) { for (int i = 0; i < n_directions; i++) { // All m's are 1 v[i] = 1 << (31 - i); } } else { int d = sobol_primitives[dim].degree; // The first direction numbers (up to the degree of the polynomial) // are simply v[i] = m[i] / 2^i (stored in Q0.32 format) for (int i = 0; i < d; i++) { v[i] = sobol_primitives[dim].m[i] << (31 - i); } // The remaining direction numbers are computed as described in // the Bratley and Fox paper. // v[i] = a[1]v[i-1] ^ a[2]v[i-2] ^ ... ^ a[v-1]v[i-d+1] ^ v[i-d] ^ // v[i-d]/2^d for (int i = d; i < n_directions; i++) { // First do the v[i-d] ^ v[i-d]/2^d part v[i] = v[i - d] ^ (v[i - d] >> d); // Now do the a[1]v[i-1] ^ a[2]v[i-2] ^ ... part // Note that the coefficients a[] are zero or one and for compactness in // the input tables they are stored as bits of a single integer. To // extract the relevant bit we use right shift and mask with 1. // For example, for a 10 degree polynomial there are ten useful bits in // a, so to get a[2] we need to right shift 7 times (to get the 8th bit // into the LSB) and then mask with 1. for (int j = 1; j < d; j++) { v[i] ^= (((sobol_primitives[dim].a >> (d - 1 - j)) & 1) * v[i - j]); } } } v += n_directions; } } // Reference model for generating Sobol numbers on the host void sobolCPU(int n_vectors, int n_dimensions, unsigned int *directions, float *output) { unsigned int *v = directions; for (int d = 0; d < n_dimensions; d++) { unsigned int X = 0; // x[0] is zero (in all dimensions) output[n_vectors * d] = 0.0; for (int i = 1; i < n_vectors; i++) { // x[i] = x[i-1] ^ v[c] // where c is the index of the rightmost zero bit in i // minus 1 (since C arrays count from zero) // In the Bratley and Fox paper this is equation (**) X ^= v[ffs(~(i - 1)) - 1]; output[i + n_vectors * d] = (float)X * k_2powneg32; } v += n_directions; } }