chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include <curand_kernel.h>
|
||||
#endif
|
||||
#ifdef __HIPCC__
|
||||
#include <hiprand_kernel.h>
|
||||
#endif
|
||||
|
||||
#include "paddle/common/enforce.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_launch_config.h"
|
||||
#include "paddle/phi/common/amp_type_traits.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "paddle/phi/kernels/binomial_kernel.h"
|
||||
#include "paddle/phi/kernels/funcs/for_range.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
__device__ __constant__ float kTailValues[] = {0.0810614667953272,
|
||||
0.0413406959554092,
|
||||
0.0276779256849983,
|
||||
0.02079067210376509,
|
||||
0.0166446911898211,
|
||||
0.0138761288230707,
|
||||
0.0118967099458917,
|
||||
0.0104112652619720,
|
||||
0.00925546218271273,
|
||||
0.00833056343336287};
|
||||
|
||||
template <typename T>
|
||||
__device__ T stirling_approx_tail(int64_t k) {
|
||||
if (k <= 9) {
|
||||
return static_cast<T>(kTailValues[static_cast<size_t>(k)]);
|
||||
}
|
||||
T kp1sq = (k + 1) * (k + 1);
|
||||
return (1.0 / 12 - (1.0 / 360 - 1.0 / 1260 / kp1sq) / kp1sq) / (k + 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__device__ int64_t btrs(
|
||||
const T n, const T p, int64_t idx, unsigned int seed, unsigned int offset) {
|
||||
int64_t k;
|
||||
T U, V, us;
|
||||
|
||||
#ifdef __NVCC__
|
||||
curandStatePhilox4_32_10_t state;
|
||||
curand_init(seed, idx, offset, &state);
|
||||
#elif __HIPCC__
|
||||
hiprandStatePhilox4_32_10_t state;
|
||||
hiprand_init(seed, idx, offset, &state);
|
||||
#endif
|
||||
|
||||
const T stddev = std::sqrt(n * p * (1 - p));
|
||||
|
||||
const T b = 1.15 + 2.53 * stddev;
|
||||
const T a = -0.0873 + 0.0248 * b + 0.01 * p;
|
||||
const T c = n * p + 0.5;
|
||||
const T v_r = 0.92 - 4.2 / b;
|
||||
const T r = p / (1 - p);
|
||||
|
||||
const T alpha = (2.83 + 5.1 / b) * stddev;
|
||||
const T m = std::floor((n + 1) * p);
|
||||
|
||||
while (1) {
|
||||
#ifdef __NVCC__
|
||||
U = static_cast<T>(curand_uniform(&state)) - 0.5;
|
||||
V = static_cast<T>(curand_uniform(&state));
|
||||
#elif __HIPCC__
|
||||
U = static_cast<T>(hiprand_uniform(&state)) - 0.5;
|
||||
V = static_cast<T>(hiprand_uniform(&state));
|
||||
#endif
|
||||
|
||||
us = 0.5 - std::abs(U);
|
||||
k = static_cast<int64_t>(std::floor((2 * a / us + b) * U + c));
|
||||
|
||||
if (k < 0 || k > n) {
|
||||
continue;
|
||||
}
|
||||
if (us >= 0.07 && V <= v_r) {
|
||||
return k;
|
||||
}
|
||||
|
||||
V = std::log(V * alpha / (a / (us * us) + b));
|
||||
T upperbound =
|
||||
((m + 0.5) * std::log((m + 1) / (r * (n - m + 1))) +
|
||||
(n + 1) * std::log((n - m + 1) / (n - k + 1)) +
|
||||
(k + 0.5) * std::log(r * (n - k + 1) / (k + 1)) +
|
||||
stirling_approx_tail<T>(m) + stirling_approx_tail<T>(n - m) -
|
||||
stirling_approx_tail<T>(k) - stirling_approx_tail<T>(n - k));
|
||||
|
||||
if (V <= upperbound) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__device__ int64_t binomial_inversion(
|
||||
const T n, const T p, int64_t idx, unsigned int seed, unsigned int offset) {
|
||||
T unif;
|
||||
T geom_sum = 0.0;
|
||||
int64_t num_geom = 0;
|
||||
T logprob = std::log1p(-p);
|
||||
|
||||
#ifdef __NVCC__
|
||||
curandStatePhilox4_32_10_t state;
|
||||
curand_init(seed, idx, offset, &state);
|
||||
#elif __HIPCC__
|
||||
hiprandStatePhilox4_32_10_t state;
|
||||
hiprand_init(seed, idx, offset, &state);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
#ifdef __NVCC__
|
||||
unif = static_cast<T>(curand_uniform(&state));
|
||||
#elif __HIPCC__
|
||||
unif = static_cast<T>(hiprand_uniform(&state));
|
||||
#endif
|
||||
T geom = std::ceil(std::log(unif) / logprob);
|
||||
geom_sum += geom;
|
||||
if (geom_sum > n) {
|
||||
break;
|
||||
}
|
||||
num_geom = num_geom + 1;
|
||||
}
|
||||
return num_geom;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void BinomialSampling(const T* n,
|
||||
const T* p,
|
||||
int64_t* out,
|
||||
const int N,
|
||||
unsigned int seed,
|
||||
unsigned int offset) {
|
||||
using MT = typename MPTypeTrait<T>::Type;
|
||||
CUDA_KERNEL_LOOP_TYPE(idx, N, int64_t) {
|
||||
MT nt = static_cast<MT>(n[idx]);
|
||||
MT pt = static_cast<MT>(p[idx]);
|
||||
if (nt <= 0.0 || pt <= 0.0) {
|
||||
out[idx] = 0;
|
||||
} else if (pt >= 1.0) {
|
||||
out[idx] = static_cast<int64_t>(nt);
|
||||
} else if (pt <= 0.5) {
|
||||
if (nt * pt >= 10.0) {
|
||||
out[idx] = btrs<MT>(nt, pt, idx, seed, offset);
|
||||
} else {
|
||||
out[idx] = binomial_inversion<MT>(nt, pt, idx, seed, offset);
|
||||
}
|
||||
} else {
|
||||
MT qprob = 1.0 - pt;
|
||||
if (nt * qprob >= 10.0) {
|
||||
out[idx] =
|
||||
static_cast<int64_t>(nt) - btrs<MT>(nt, qprob, idx, seed, offset);
|
||||
} else {
|
||||
out[idx] = static_cast<int64_t>(nt) -
|
||||
binomial_inversion<MT>(nt, qprob, idx, seed, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Context>
|
||||
void BinomialKernel(const Context& dev_ctx,
|
||||
const DenseTensor& count,
|
||||
const DenseTensor& prob,
|
||||
DenseTensor* out) {
|
||||
const T* count_data = count.data<T>();
|
||||
const T* prob_data = prob.data<T>();
|
||||
int64_t* out_data = dev_ctx.template Alloc<int64_t>(out);
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t size = count.numel();
|
||||
|
||||
const int kMaxBlockDim = 256;
|
||||
|
||||
int block_size = std::min(kMaxBlockDim, dev_ctx.GetMaxThreadsPerBlock());
|
||||
dim3 dim_block(block_size);
|
||||
int64_t grid_64 = (size + block_size - 1) / block_size;
|
||||
PADDLE_ENFORCE_LE_UINT32_MAX(grid_64, "binomial grid.x");
|
||||
dim3 dim_grid(static_cast<uint32_t>(grid_64));
|
||||
backends::gpu::LimitGridDim(dev_ctx, &dim_grid);
|
||||
|
||||
auto gen_cuda = dev_ctx.GetGenerator();
|
||||
auto seed_offset = gen_cuda->IncrementOffset(20);
|
||||
uint64_t seed = seed_offset.first;
|
||||
uint64_t offset = seed_offset.second;
|
||||
BinomialSampling<T><<<dim_grid, dim_block>>>(
|
||||
count_data, prob_data, out_data, size, seed, offset);
|
||||
}
|
||||
|
||||
} // namespace phi
|
||||
|
||||
PD_REGISTER_KERNEL(binomial,
|
||||
GPU,
|
||||
ALL_LAYOUT,
|
||||
phi::BinomialKernel,
|
||||
float,
|
||||
double,
|
||||
phi::float16,
|
||||
phi::bfloat16) {
|
||||
kernel->OutputAt(0).SetDataType(phi::DataType::INT64);
|
||||
}
|
||||
Reference in New Issue
Block a user