Files
deeplearning4j--deeplearning4j/libnd4j/include/graph/RandomGenerator.h
T
2026-07-13 12:47:05 +08:00

410 lines
12 KiB
C++

/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* 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.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
#ifndef LIBND4J_GRAPH_RNG_H
#define LIBND4J_GRAPH_RNG_H
#include <array/DataTypeUtils.h>
#include <helpers/logger.h>
#include <math/templatemath.h>
#include <system/op_boilerplate.h>
#include <types/u32.h>
#include <types/u64.h>
#include <chrono>
#include <stdexcept>
namespace sd {
namespace graph {
#if defined(__CUDACC__)
#ifndef __JAVACPP_HACK__
class SD_LIB_EXPORT CudaManagedRandomGenerator {
private:
protected:
void *devHolder;
public:
void *operator new(size_t len) {
void *ptr;
auto res = cudaHostAlloc(&ptr, len, cudaHostAllocDefault);
if (res != 0) THROW_EXCEPTION("CudaManagedRandomGenerator: failed to allocate memory");
return ptr;
}
void operator delete(void *ptr) { cudaFreeHost(ptr); }
};
class SD_LIB_EXPORT RandomGenerator : public CudaManagedRandomGenerator {
private:
u64 _rootState;
u64 _nodeState;
static SD_INLINE LongType currentMilliseconds();
public:
SD_INLINE RandomGenerator(LongType rootSeed = 0, LongType nodeSeed = 0);
SD_INLINE SD_HOST void setStates(LongType rootSeed, LongType nodeState = 0);
template <typename T>
SD_INLINE SD_HOST_DEVICE T relativeT(LongType index, T from, T to);
template <typename T>
SD_INLINE SD_HOST_DEVICE T relativeT(LongType index);
SD_INLINE SD_HOST_DEVICE int relativeInt(LongType index);
SD_INLINE SD_HOST_DEVICE LongType relativeLong(LongType index);
SD_INLINE SD_HOST_DEVICE void rewindH(uint64_t steps);
SD_INLINE SD_HOST void setSeed(int seed) { _nodeState._ulong = static_cast<uint64_t>(seed); }
SD_INLINE SD_HOST void setSeed(uint64_t seed) { _nodeState._ulong = seed; }
SD_INLINE SD_HOST_DEVICE LongType rootState() { return _rootState._long; }
SD_INLINE SD_HOST_DEVICE LongType nodeState() { return _nodeState._long; }
SD_INLINE SD_HOST_DEVICE uint32_t xoroshiro32(uint64_t index);
SD_INLINE SD_HOST_DEVICE uint64_t xoroshiro64(uint64_t index);
};
#endif
#else
class SD_LIB_EXPORT RandomGenerator {
private:
u64 _rootState;
u64 _nodeState;
static SD_INLINE LongType currentMilliseconds();
public:
SD_INLINE RandomGenerator(LongType rootSeed = 0, LongType nodeSeed = 0);
SD_INLINE SD_HOST void setStates(LongType rootSeed, LongType nodeState = 0);
template <typename T>
SD_INLINE SD_HOST_DEVICE T relativeT(LongType index, T from, T to);
template <typename T>
SD_INLINE SD_HOST_DEVICE T relativeT(LongType index);
SD_INLINE SD_HOST_DEVICE int relativeInt(LongType index);
SD_INLINE SD_HOST_DEVICE LongType relativeLong(LongType index);
SD_INLINE SD_HOST_DEVICE void rewindH(uint64_t steps);
SD_INLINE SD_HOST void setSeed(int seed) { _nodeState._ulong = static_cast<uint64_t>(seed); }
SD_INLINE SD_HOST void setSeed(uint64_t seed) { _nodeState._ulong = seed; }
SD_INLINE SD_HOST_DEVICE LongType rootState() { return _rootState._long; }
SD_INLINE SD_HOST_DEVICE LongType nodeState() { return _nodeState._long; }
SD_INLINE SD_HOST_DEVICE uint32_t xoroshiro32(uint64_t index);
SD_INLINE SD_HOST_DEVICE uint64_t xoroshiro64(uint64_t index);
};
#endif
// Implementation of member functions (common for both CUDA and non-CUDA versions)
SD_INLINE RandomGenerator::RandomGenerator(LongType rootSeed, LongType nodeSeed) {
_rootState._long = (rootSeed == 0) ? currentMilliseconds() : rootSeed;
_nodeState._long = (nodeSeed != 0) ? nodeSeed : 1298567341LL;
}
SD_INLINE void RandomGenerator::setStates(LongType rootSeed, LongType nodeSeed) {
_rootState._long = (rootSeed == 0) ? currentMilliseconds() : rootSeed;
_nodeState._long = (nodeSeed != 0) ? nodeSeed : 1298567341LL;
}
SD_INLINE LongType RandomGenerator::currentMilliseconds() {
auto s = std::chrono::system_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::milliseconds>(s).count();
}
// Template specializations for relativeT
#ifdef HAS_FLOAT32
template <>
SD_INLINE SD_HOST_DEVICE float RandomGenerator::relativeT<float>(LongType index) {
u32 u;
u._u32 = (0x3f800000 | (this->xoroshiro32(index) >> 9));
return u._f32 - 1.0f;
}
#endif
#ifdef HAS_DOUBLE
template <>
SD_INLINE SD_HOST_DEVICE double RandomGenerator::relativeT<double>(LongType index) {
#ifdef __DOUBLE_RNG__
u64 u;
u._ulong = ((UINT64_C(0x3FF) << 52) | (this->xoroshiro64(index) >> 12));
return u._double - 1.0;
#else
return (double)relativeT<float>(index);
#endif
}
#endif
// Use unsigned long instead of uint64_t to avoid redefinition issues
#ifdef HAS_UINT64
template <>
SD_INLINE SD_HOST_DEVICE unsigned long RandomGenerator::relativeT<unsigned long>(LongType index) {
return this->xoroshiro64(index);
}
#endif
#ifdef HAS_UINT32
template <>
SD_INLINE SD_HOST_DEVICE uint32_t RandomGenerator::relativeT<uint32_t>(LongType index) {
return this->xoroshiro32(index);
}
#endif
#ifdef HAS_INT32
template <>
SD_INLINE SD_HOST_DEVICE int RandomGenerator::relativeT<int>(LongType index) {
auto r = static_cast<int>(relativeT<uint32_t>(index));
return r <= DataTypeUtils::max<int>() ? r : r % DataTypeUtils::max<int>();
}
#endif
#ifdef HAS_INT64
template <>
SD_INLINE SD_HOST_DEVICE LongType RandomGenerator::relativeT<LongType>(LongType index) {
auto r = static_cast<sd::LongType>(relativeT<unsigned long>(index));
return r <= DataTypeUtils::max<LongType>() ? r : r % DataTypeUtils::max<LongType>();
}
#endif
// Additional template specializations for integer types with single parameter
#ifdef HAS_INT8
template <>
SD_INLINE SD_HOST_DEVICE int8_t RandomGenerator::relativeT<int8_t>(LongType index) {
// Return a value between 0 and max for int8_t
float t = this->relativeT<float>(index);
return static_cast<int8_t>(t * DataTypeUtils::max<int8_t>());
}
#endif
#ifdef HAS_UINT8
template <>
SD_INLINE SD_HOST_DEVICE uint8_t RandomGenerator::relativeT<uint8_t>(LongType index) {
float t = this->relativeT<float>(index);
return static_cast<uint8_t>(t * DataTypeUtils::max<uint8_t>());
}
#endif
#ifdef HAS_INT16
template <>
SD_INLINE SD_HOST_DEVICE int16_t RandomGenerator::relativeT<int16_t>(LongType index) {
float t = this->relativeT<float>(index);
return static_cast<int16_t>(t * DataTypeUtils::max<int16_t>());
}
#endif
#ifdef HAS_UINT16
template <>
SD_INLINE SD_HOST_DEVICE uint16_t RandomGenerator::relativeT<uint16_t>(LongType index) {
float t = this->relativeT<float>(index);
return static_cast<uint16_t>(t * DataTypeUtils::max<uint16_t>());
}
#endif
#ifdef HAS_BOOL
template <>
SD_INLINE SD_HOST_DEVICE bool RandomGenerator::relativeT<bool>(LongType index) {
float t = this->relativeT<float>(index);
return t > 0.5f;
}
#endif
#ifdef HAS_FLOAT16
template <>
SD_INLINE SD_HOST_DEVICE float16 RandomGenerator::relativeT<float16>(LongType index) {
return static_cast<float16>(relativeT<float>(index));
}
#endif
#ifdef HAS_BFLOAT16
template <>
SD_INLINE SD_HOST_DEVICE bfloat16 RandomGenerator::relativeT<bfloat16>(LongType index) {
return static_cast<bfloat16>(relativeT<float>(index));
}
#endif
// Generic template for relativeT with range parameters
template <typename T>
SD_INLINE SD_HOST_DEVICE T RandomGenerator::relativeT(LongType index, T from, T to) {
auto t = this->relativeT<T>(index);
return from + T(t * (to - from));
}
// Specializations for relativeT with range parameters
#ifdef HAS_INT64
template <>
SD_INLINE SD_HOST_DEVICE LongType RandomGenerator::relativeT(LongType index, LongType from, LongType to) {
auto t = this->relativeT<double>(index);
return from + LongType(t * (to - from));
}
#endif
#ifdef HAS_INT32
template <>
SD_INLINE SD_HOST_DEVICE int RandomGenerator::relativeT(LongType index, int from, int to) {
auto t = this->relativeT<float>(index);
return from + int(t * (to - from));
}
#endif
// Template specializations for integer types with range parameters
#ifdef HAS_INT8
template <>
SD_INLINE SD_HOST_DEVICE int8_t RandomGenerator::relativeT(LongType index, int8_t from, int8_t to) {
// Use float for intermediate calculation to get proper distribution
float t = this->relativeT<float>(index);
return from + static_cast<int8_t>(t * (to - from));
}
#endif
#ifdef HAS_UINT8
template <>
SD_INLINE SD_HOST_DEVICE uint8_t RandomGenerator::relativeT(LongType index, uint8_t from, uint8_t to) {
float t = this->relativeT<float>(index);
return from + static_cast<uint8_t>(t * (to - from));
}
#endif
#ifdef HAS_INT16
template <>
SD_INLINE SD_HOST_DEVICE int16_t RandomGenerator::relativeT(LongType index, int16_t from, int16_t to) {
float t = this->relativeT<float>(index);
return from + static_cast<int16_t>(t * (to - from));
}
#endif
#ifdef HAS_UINT16
template <>
SD_INLINE SD_HOST_DEVICE uint16_t RandomGenerator::relativeT(LongType index, uint16_t from, uint16_t to) {
float t = this->relativeT<float>(index);
return from + static_cast<uint16_t>(t * (to - from));
}
#endif
#ifdef HAS_BOOL
template <>
SD_INLINE SD_HOST_DEVICE bool RandomGenerator::relativeT(LongType index, bool from, bool to) {
float t = this->relativeT<float>(index);
return t > 0.5f ? to : from;
}
#endif
#ifdef HAS_FLOAT16
template <>
SD_INLINE SD_HOST_DEVICE float16 RandomGenerator::relativeT(LongType index, float16 from, float16 to) {
float t = this->relativeT<float>(index);
return from + static_cast<float16>(t * (to - from));
}
#endif
#ifdef HAS_BFLOAT16
template <>
SD_INLINE SD_HOST_DEVICE bfloat16 RandomGenerator::relativeT(LongType index, bfloat16 from, bfloat16 to) {
float t = this->relativeT<float>(index);
return from + static_cast<bfloat16>(t * (to - from));
}
#endif
#ifdef HAS_FLOAT32
template <>
SD_INLINE SD_HOST_DEVICE float RandomGenerator::relativeT(LongType index, float from, float to) {
auto t = this->relativeT<float>(index);
return from + (t * (to - from));
}
#endif
#ifdef HAS_DOUBLE
template <>
SD_INLINE SD_HOST_DEVICE double RandomGenerator::relativeT(LongType index, double from, double to) {
auto t = this->relativeT<double>(index);
return from + (t * (to - from));
}
#endif
// Generic fallback template - only compiled if no specialization exists
template <typename T>
SD_INLINE SD_HOST_DEVICE T RandomGenerator::relativeT(LongType index) {
return static_cast<T>(relativeT<float>(index));
}
SD_INLINE SD_HOST_DEVICE int RandomGenerator::relativeInt(LongType index) {
#ifdef HAS_UINT32
auto r = static_cast<int>(relativeT<uint32_t>(index));
return r <= DataTypeUtils::max<int>() ? r : r % DataTypeUtils::max<int>();
#else
return 0; // Fallback if no uint32_t
#endif
}
SD_INLINE SD_HOST_DEVICE LongType RandomGenerator::relativeLong(LongType index) {
#ifdef HAS_UINT64
auto r = static_cast<LongType>(relativeT<unsigned long>(index));
return r <= DataTypeUtils::max<LongType>() ? r : r % DataTypeUtils::max<LongType>();
#else
return 0; // Fallback if no uint64_t
#endif
}
// Helper functions
static SD_INLINE SD_HOST_DEVICE uint32_t rotl(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); }
static SD_INLINE SD_HOST_DEVICE uint64_t rotl(const uint64_t x, int k) { return (x << k) | (x >> (64 - k)); }
SD_INLINE SD_HOST_DEVICE uint32_t RandomGenerator::xoroshiro32(uint64_t index) {
auto s0 = _rootState._ulong;
auto s1 = _nodeState._ulong;
s0 |= ((index + 2) * (s1 + 24243287));
s1 ^= ((index + 2) * (s0 + 723829));
unsigned long val = s1 ^ s0;
int *pHalf = reinterpret_cast<int *>(&val);
return rotl(*pHalf * 0x9E3779BB, 5) * 5;
}
SD_INLINE SD_HOST_DEVICE uint64_t RandomGenerator::xoroshiro64(uint64_t index) {
uint64_t upper = ((uint64_t)xoroshiro32(index)) << 32;
// Use direct bit manipulation instead of sd_rotl to avoid template issues
uint64_t rotated = (index << 32) | (index >> 32);
uint32_t lower = xoroshiro32(rotated);
return upper + lower;
}
SD_INLINE SD_HOST_DEVICE void RandomGenerator::rewindH(uint64_t steps) {
auto s0 = _nodeState._du32._v0;
auto s1 = _nodeState._du32._v1;
s1 ^= s0;
_nodeState._du32._v0 = rotl(s0, 26) ^ s1 ^ (s1 << 9);
_nodeState._du32._v1 = rotl(s1, 13);
_nodeState._long ^= (steps ^ 0xdeadbeef);
}
} // namespace graph
} // namespace sd
#endif // LIBND4J_GRAPH_RNG_H