/* ****************************************************************************** * * * 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 ******************************************************************************/ // // @author raver119@gmail.com // #ifndef LIBND4J_RANDOM_OPS_H #define LIBND4J_RANDOM_OPS_H #include // since we can't inherit/overwrite static methods - we just define default impls #define method_idx \ static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *rng, \ T *extraParams) { \ return static_cast(-1.0f); \ } #define method_X \ static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, \ sd::graph::RandomGenerator *rng, T *extraParams) { \ return static_cast(-2.0f); \ } #define method_XY \ static SD_INLINE SD_HOST_DEVICE T op(T valueX, T valueY, sd::LongType idx, sd::LongType length, \ sd::graph::RandomGenerator *rng, T *extraParams) { \ return static_cast(-3.0f); \ } #define no_exec_special \ static const bool requiresSpecial = false; \ static inline void specialOp(sd::Pointer state, const T *x, const sd::LongType *xShapeBuffer, const T *y, \ const sd::LongType *yShapeBuffer, T *z, const sd::LongType *zShapeBuffer, \ T *extraArguments) {} #ifdef __CUDACC__ #define no_exec_special_cuda \ static SD_INLINE SD_DEVICE void specialOpCuda(sd::Pointer state, T const *x, sd::LongType const *xShapeBuffer, \ T const *y, sd::LongType const *yShapeBuffer, T *z, \ sd::LongType const *zShapeBuffer, T *extraArguments) { printf("No special op for this method\n"); } #else #define no_exec_special_cuda #endif #include #include #include namespace randomOps { /** * This Op merges two arrays per-element, if probability meets threshold */ template class ProbablisticMerge { public: no_exec_special no_exec_special_cuda method_idx method_X static SD_INLINE SD_HOST_DEVICE T op(T valueX, T valueY, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T threshold = extraParams[0]; T randVal = helper->relativeT(idx); return randVal <= threshold ? valueY : valueX; } }; /** * This Op produces random values within specified boundaries. Disribution is uniform */ template class UniformDistribution { public: no_exec_special no_exec_special_cuda method_XY method_X static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { return helper->relativeT(idx, extraParams[0], extraParams[1]); } }; /** * This op produces single bernoulli trial */ template class BernoulliDistribution { public: no_exec_special no_exec_special_cuda method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { return extraParams[0] >= helper->relativeT(idx) ? (T)1.0f : (T)0.0f; } static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { return valueX >= helper->relativeT(idx) ? (T)1.0f : (T)0.0f; } }; /** * This op produces single bernoulli trial */ template class ExponentialDistribution { public: no_exec_special no_exec_special_cuda method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T lambda = extraParams[0]; T x = helper->relativeT(idx, sd::DataTypeUtils::min_positive(), T(1.f) - sd::DataTypeUtils::template min_positive()); // x from (0, 1) without bounds T xVal = -sd::math::sd_log(x); return xVal <= (T)0.f ? (T)0.f : xVal / lambda; // pow((T) M_E, -(lambda * x)); } static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T lambda = extraParams[0]; return valueX <= (T)0.f ? (T)0.f : (T)(valueX / lambda); // 1.f - sd::math::sd_exp(-lambda * valueX); //pow((T) M_E, -(lambda * valueX)); } }; template class PoissonDistribution { public: no_exec_special no_exec_special_cuda method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T lambda = extraParams[0]; T x = helper->relativeT(idx, -sd::DataTypeUtils::template max() / 10, sd::DataTypeUtils::template max() / 10); return x <= (T)0.f ? (T)0.f : sd::math::sd_igammac(sd::math::sd_floor(x), lambda); } static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T lambda = extraParams[0]; return valueX <= (T)0.f ? (T)0.f : (T)sd::math::sd_igammac(sd::math::sd_floor(valueX), lambda); } }; template class GammaDistribution { public: no_exec_special no_exec_special_cuda method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T alpha = extraParams[0]; T beta = extraParams[1]; T x = helper->relativeT(idx, -sd::DataTypeUtils::template max() / 10, sd::DataTypeUtils::template max() / 10); return x <= (T)0.f ? (T)0.f : sd::math::sd_igamma(alpha, x * beta); } static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T alpha = extraParams[0]; T beta = extraParams[1]; return valueX <= (T)0.f ? (T)0.f : sd::math::sd_igamma(alpha, beta * valueX); } }; /** * Basic DropOut/DropConnect Op */ template class DropOut { public: no_exec_special no_exec_special_cuda method_idx method_XY // please note: prob is chance to retain original value static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T randVal = helper->relativeT(idx); return randVal >= extraParams[0] ? (T)0.0f : valueX; } }; template class AlphaDropOut { public: no_exec_special no_exec_special_cuda method_idx method_XY // please note: prob is chance to retain original value static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T randVal = helper->relativeT(idx); // extraParams[0] == p // [1] = a // [2] = b // [3] = alphaPrime return randVal >= extraParams[0] ? (T)extraParams[1] * extraParams[3] + extraParams[2] : extraParams[1] * valueX + extraParams[2]; } }; /** * Inverted DropOut implementation, used in DL4j */ template class DropOutInverted { public: no_exec_special no_exec_special_cuda method_idx method_XY // please note: prob is chance to retain original value static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T prob = extraParams[0]; T randVal = helper->relativeT(idx); return randVal >= prob ? (T)0.0f : valueX / prob; } }; template class Linspace { public: no_exec_special no_exec_special_cuda method_X method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { T from = extraParams[0]; T to = extraParams[1]; T step = extraParams[2]; if (step == static_cast(0.0f)) { step = (T)idx / ((T)length - (T)1.0f); return from * ((T)1.0f - step) + step * to; } return from + (idx * step); } }; template class ExponentialDistributionInv { // inverse exponential distribution public: no_exec_special no_exec_special_cuda method_XY static SD_INLINE SD_HOST_DEVICE T op(sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { // For integer types, we need to use float for intermediate calculations // since exponential distribution requires floating point math if constexpr (std::is_integral::value && !std::is_same::value) { float lambdaFloat = 1.0f; // Default lambda for integer types if (extraParams != nullptr && sizeof(T) >= sizeof(float)) { lambdaFloat = static_cast(extraParams[0]); } if (lambdaFloat == 0.0f) lambdaFloat = 1.0f; // Avoid division by zero // Get a float random value in (0, 1) float x = helper->relativeT(idx); // Ensure x is in the valid range for log if (x <= 0.0f) x = sd::DataTypeUtils::min_positive(); if (x >= 1.0f) x = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - x) / lambdaFloat; // Scale the result to fit in the integer type's range // Map [0, inf) to [0, max(T)] if (result < 0.0f) result = 0.0f; float maxVal = static_cast(sd::DataTypeUtils::max()); if (result > maxVal) result = maxVal; return static_cast(result); } else if constexpr (std::is_same::value) { // For bool type, use float intermediate and return based on threshold float x = helper->relativeT(idx); if (x <= 0.0f) x = sd::DataTypeUtils::min_positive(); if (x >= 1.0f) x = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - x); // For bool, return true if result > 0.5, false otherwise return result > 0.5f; } else if constexpr (std::is_same::value || std::is_same::value) { // For half precision types, use float for calculation float lambda = extraParams != nullptr ? static_cast(extraParams[0]) : 1.0f; if (lambda == 0.0f) lambda = 1.0f; float x = helper->relativeT(idx); if (x <= 0.0f) x = sd::DataTypeUtils::min_positive(); if (x >= 1.0f) x = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - x) / lambda; return static_cast(result); } else { // For floating point types (float, double), use the original implementation T lambda = extraParams[0]; if (lambda == static_cast(0)) lambda = static_cast(1); // Avoid division by zero T x = helper->relativeT(idx, sd::DataTypeUtils::min_positive(), static_cast(1) - sd::DataTypeUtils::min_positive()); return -sd::math::sd_log(static_cast(1) - x) / lambda; } } static SD_INLINE SD_HOST_DEVICE T op(T valueX, sd::LongType idx, sd::LongType length, sd::graph::RandomGenerator *helper, T *extraParams) { if constexpr (std::is_integral::value && !std::is_same::value) { float lambdaFloat = 1.0f; if (extraParams != nullptr && sizeof(T) >= sizeof(float)) { lambdaFloat = static_cast(extraParams[0]); } if (lambdaFloat == 0.0f) lambdaFloat = 1.0f; float floatValueX = static_cast(valueX) / static_cast(sd::DataTypeUtils::max()); // Ensure value is in valid range if (floatValueX <= 0.0f) floatValueX = sd::DataTypeUtils::min_positive(); if (floatValueX >= 1.0f) floatValueX = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - floatValueX) / lambdaFloat; if (result < 0.0f) result = 0.0f; float maxVal = static_cast(sd::DataTypeUtils::max()); if (result > maxVal) result = maxVal; return static_cast(result); } else if constexpr (std::is_same::value) { float floatValueX = valueX ? 1.0f : 0.0f; if (floatValueX <= 0.0f) floatValueX = sd::DataTypeUtils::min_positive(); if (floatValueX >= 1.0f) floatValueX = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - floatValueX); return result > 0.5f; } else if constexpr (std::is_same::value || std::is_same::value) { float lambda = extraParams != nullptr ? static_cast(extraParams[0]) : 1.0f; if (lambda == 0.0f) lambda = 1.0f; float floatValueX = static_cast(valueX); if (floatValueX <= 0.0f) floatValueX = sd::DataTypeUtils::min_positive(); if (floatValueX >= 1.0f) floatValueX = 1.0f - sd::DataTypeUtils::min_positive(); float result = -sd::math::sd_log(1.0f - floatValueX) / lambda; return static_cast(result); } else { T lambda = extraParams[0]; if (lambda == static_cast(0)) lambda = static_cast(1); return -sd::math::sd_log(static_cast(1) - valueX) / lambda; } } }; } // namespace randomOps #endif // LIBND4J_RANDOM_OPS_H