/* ****************************************************************************** * * * 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 ******************************************************************************/ #pragma once #ifndef OPS_H_ #define OPS_H_ #include #include #include #include #include #include #include #include #include #include #include "helpers/unicode.h" #include "op_macros_meta.h" // ============================================================================= // CONSTANTS // ============================================================================= #define SELU_ALPHA 1.6732632423543772848170429916717 #define SELU_LAMBDA 1.0507009873554804934193349852946 #define SD_STRING_ASSIGN_TEMP_BUFFER_BYTES 256 namespace simdOps { // ============================================================================= // BINARY ARITHMETIC OPERATIONS // ============================================================================= DECLARE_BINARY_MATH_OP(Add, sd_add) DECLARE_BINARY_MATH_OP(Subtract, sd_subtract) DECLARE_BINARY_MATH_OP(Multiply, sd_multiply) DECLARE_BINARY_MATH_OP(Divide, sd_divide) // Reverse operations DECLARE_REVERSE_BINARY_MATH_OP(ReverseSubtract, sd_subtract, 0.f) DECLARE_REVERSE_BINARY_MATH_OP(ReverseDivide, sd_divide, 1) // HardTanh - uses complex conditional macro DECLARE_UNARY_COMPLEX_CONDITIONAL_OP(HardTanh, d1 < static_cast(-1), static_cast(-1), d1 > static_cast(1), static_cast(1), d1) DECLARE_UNARY_CONDITIONAL_OP(RectifiedTanhDerivative, d1 > static_cast(0.f), sd::math::sd_tanhderivative(d1), static_cast(0.f)) DECLARE_BINARY_MATH_OP_XZ(Atan2, sd_atan2) DECLARE_BINARY_MATH_OP_WITH_STARTING(PowDerivative, static_cast(d2) * sd::math::sd_pow(d1 COMMA static_cast(d2) - static_cast(1.f)), static_cast(d1), params[0] * sd::math::sd_pow(d1 COMMA static_cast(params[0]) - static_cast(1.f)), static_cast(0) ) DECLARE_BINARY_COPY_OP(AMaxPairwise, sd::math::sd_abs(static_cast(d1)) > sd::math::sd_abs(static_cast(d2)) ? static_cast(d1) : static_cast(d2), sd::math::sd_abs(static_cast(d1)) > sd::math::sd_abs(static_cast(d2)) ? static_cast(d1) : static_cast(d2), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_COPY_OP(AMinPairwise, sd::math::sd_abs(static_cast(d1)) < sd::math::sd_abs(static_cast(d2)) ? static_cast(d1) : static_cast(d2), sd::math::sd_abs(static_cast(d1)) < sd::math::sd_abs(static_cast(d2)) ? static_cast(d1) : static_cast(d2), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_COPY_OP(MaxPairwise, sd::math::sd_max(static_cast(d1) COMMA static_cast(d2)), sd::math::sd_max(static_cast(d1) COMMA static_cast(d2)), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_COPY_OP(MinPairwise, sd::math::sd_min(d1 COMMA d2), sd::math::sd_min(d1 COMMA d2), static_cast(d1), static_cast(d1) ) DECLARE_REDUCE3_OP_WITH_BOOL_SUPPORT(CosineSimilarity, // Bool logic (for boolean input types) extraParamsRef[0] += static_cast(static_cast(d1) * static_cast(d1)); \ extraParamsRef[1] += static_cast(static_cast(d2) * static_cast(d2)); \ return static_cast(static_cast(d1) * static_cast(d2));, // Normal logic (for non-boolean types) extraParamsRef[0] += static_cast(d1 * d1); \ extraParamsRef[1] += static_cast(d2 * d2); \ return static_cast(d1 * d2);, 2, // extraParamsLen 0.0f, // starting value // Post process - calculate cosine similarity from dot product and norms reduction / (sd::math::sd_sqrt(extraParamsRef[0]) * sd::math::sd_sqrt(extraParamsRef[1])) ) DECLARE_BINARY_MATH_OP(IGamma, sd_igamma) DECLARE_BINARY_MATH_OP(IGammac, sd_igammac) DECLARE_BINARY_COPY_OP(LogX, sd::math::sd_log(d1) / sd::math::sd_log(d2), sd::math::sd_log(d1) / sd::math::sd_log(d2), static_cast(d1), sd::math::sd_log(d1) / sd::math::sd_log(params[0]) ) // ASinhDerivative - uses complex math expression macro DECLARE_UNARY_COMPLEX_MATH_OP(ASinhDerivative, static_cast(1.f) / (sd::math::sd_sqrt(sd::math::sd_pow(d1, static_cast(2.f)) + static_cast(1.f)))) // ACoshDerivative - uses complex math expression macro DECLARE_UNARY_COMPLEX_MATH_OP(ACoshDerivative, static_cast(1.f) / (sd::math::sd_sqrt(d1 - static_cast(1.f)) * sd::math::sd_sqrt(d1 + static_cast(1.f)))) // Power operations DECLARE_POWER_OP(Pow, sd_pow) DECLARE_REVERSE_BINARY_MATH_OP(ReversePow, sd_pow, 1) DECLARE_UNARY_SIMPLE_OP(TanDerivative, static_cast(1.f) / sd::math::sd_pow(sd::math::sd_cos(d1) COMMA static_cast(2.0f))) DECLARE_SQUARED_SUBTRACT_OP(SquaredSubtract, sd_subtract) DECLARE_SQUARED_REVERSE_SUBTRACT_OP(SquaredReverseSubtract, sd_subtract) // ============================================================================= // COMPARISON OPERATIONS // ============================================================================= DECLARE_COMPARISON_OP(EqualTo, ==) DECLARE_COMPARISON_OP(NotEqualTo, !=) DECLARE_COMPARISON_OP(GreaterThan, >) DECLARE_COMPARISON_OP(GreaterThanOrEqual, >=) DECLARE_COMPARISON_OP(LessThan, <) DECLARE_COMPARISON_OP(LessThanOrEqual, <=) // ============================================================================= // UNARY MATH OPERATIONS // ============================================================================= DECLARE_UNARY_MATH_OP(Abs, sd_abs) DECLARE_UNARY_MATH_OP(Ceiling, sd_ceil) DECLARE_UNARY_MATH_OP(Cosine, sd_cos) DECLARE_UNARY_MATH_OP(Exp, sd_exp) DECLARE_UNARY_MATH_OP(Floor, sd_floor) DECLARE_UNARY_MATH_OP(Log, sd_log) DECLARE_UNARY_MATH_OP(Sin, sd_sin) DECLARE_UNARY_MATH_OP(Tanh, sd_tanh) DECLARE_UNARY_MATH_OP(Sigmoid, sd_sigmoid) DECLARE_UNARY_SIMPLE_OP(Neg, -d1) DECLARE_UNARY_SIMPLE_OP(Square, d1 * d1) DECLARE_UNARY_SIMPLE_OP(Cube, d1 * d1 * d1) DECLARE_UNARY_SIMPLE_OP(Identity, d1) DECLARE_UNARY_SIMPLE_OP(OneMinus, static_cast(1) - d1) DECLARE_UNARY_SIMPLE_OP(Reciprocal, static_cast(1) / d1) // ============================================================================= // CONDITIONAL OPERATIONS // ============================================================================= DECLARE_UNARY_CONDITIONAL_OP(Sign, (d1 > static_cast(0)) - (d1 < static_cast(0)), static_cast(1), static_cast(-1)) DECLARE_UNARY_CONDITIONAL_OP(HardTanhDerivative, ((d1 >= static_cast(-1.f) && d1 <= static_cast(1.f)) ? static_cast(1.f) : static_cast(0.f)), d1, d1) DECLARE_UNARY_CONDITIONAL_OP(HardSigmoidDerivative, d1 < static_cast(-2.5f) || d1 > static_cast(2.5f) ? static_cast(0.f) : static_cast(0.2f), d1, d1) DECLARE_BINARY_MATH_OP(Remainder, sd_remainder) DECLARE_BINARY_MATH_OP(FMod, sd_fmod) DECLARE_SAFE_DIVISION_OP(DivideNoNan, d2 == static_cast(0)) DECLARE_SAFE_DIVISION_OP(SafeDivide, d2 == static_cast(0)) // Floor division: DECLARE_FLOOR_DIVISION_OP(FloorDiv, sd_floor) DECLARE_BINARY_MATH_OP_WITH_STARTING(TruncateDiv, static_cast(sd::math::sd_divide(static_cast(d1), static_cast(d2))), static_cast(d1), static_cast(sd::math::sd_divide(static_cast(d1), static_cast(params[0]))), static_cast(1) ) DECLARE_BINARY_MATH_OP_WITH_STARTING(TruncateMod, static_cast(static_cast(d1) % static_cast(d2)), static_cast(d1), static_cast(static_cast(d1) % static_cast(params[0])), static_cast(0) ) DECLARE_UNARY_IDENTITY_OP(Copy) template class FloorMod { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2) { Z m = sd::math::sd_fmod(d1, d2); return (d1 < static_cast(0)) == (d2 < static_cast(0)) ? m : sd::math::sd_fmod(m + static_cast(d2), d2); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2, Z *params) { return op_logic(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1) { return static_cast(d1); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y *params) { Z m = sd::math::sd_fmod(d1, params[0]); return (d1 < static_cast(0)) == (params[0] < static_cast(0)) ? m : sd::math::sd_fmod(m + static_cast(params[0]), params[0]); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, Y d2) { return op_logic(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, Y d2, Z *params) { return op_logic(d1, d2, params); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1) { return op_logic(d1); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, Y *params) { return op_logic(d1, params); } public: static SD_HOST_DEVICE SD_INLINE Z op(X d1, Y d2) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2); else return op_simd(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op(X d1, Y d2, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } static SD_HOST_DEVICE SD_INLINE Z op(X d1) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1); else return op_simd(d1); } static SD_HOST_DEVICE SD_INLINE Z op(X d1, Y *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, params); else return op_simd(d1, params); } SD_HOST_DEVICE SD_INLINE static X startingValue() { return static_cast(0); } }; DECLARE_BINARY_COPY_OP(CopyPws, static_cast(d2), static_cast(d2), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_COPY_OP(Copy2, static_cast(d2), static_cast(d2), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_COPY_OP(Axpy, static_cast(d2 + d1), params[0] * static_cast(d1) + static_cast(d2), static_cast(d1), static_cast(d1) ) DECLARE_BINARY_PARAM_OP(LstmClip, [&]() { X _v = static_cast(d2); if (d1 > _v) return static_cast(_v); else if (d1 < -_v) return static_cast(-_v); else return static_cast(d1); }(), no_op_exec_special no_op_exec_special_cuda ) DECLARE_BINARY_PARAM_OP(Step, (d1 > static_cast(d2) ? static_cast(1) : static_cast(0)), no_op_exec_special_same no_op_exec_special_same_cuda ) DECLARE_BINARY_PARAM_OP(SXELogitsSmoother, static_cast(d1 * (static_cast(1.f) - static_cast(d2)) + static_cast(0.5f) * static_cast(d2)), // no special boilerplate needed ) DECLARE_UNARY_MATH_OP(Round, sd_round) DECLARE_UNARY_MATH_OP(Rint, sd_rint) DECLARE_UNARY_MATH_OP(Erf, sd_erf) DECLARE_UNARY_MATH_OP(Erfc, sd_erfc) DECLARE_UNARY_MATH_OP(ASin, sd_asin) DECLARE_UNARY_MATH_OP(ACos, sd_acos) DECLARE_UNARY_MATH_OP(ATan, sd_atan) DECLARE_UNARY_MATH_OP(ATanh, sd_atanh) DECLARE_UNARY_MATH_OP(ASinh, sd_asinh) DECLARE_UNARY_MATH_OP(ACosh, sd_acosh) DECLARE_UNARY_MATH_OP(Sinh, sd_sinh) DECLARE_UNARY_MATH_OP(Cosh, sd_cosh) DECLARE_UNARY_MATH_OP(Tan, sd_tan) DECLARE_UNARY_MATH_OP(SoftSign, sd_softsign) // Log1p - simple math expression DECLARE_UNARY_SIMPLE_OP(Log1p, sd::math::sd_log(1 + d1)) // Expm1 - simple math expression DECLARE_UNARY_SIMPLE_OP(Expm1, sd::math::sd_exp(d1) - static_cast(1)) // StabilizeFP16 - conditional operation DECLARE_UNARY_CONDITIONAL_OP(StabilizeFP16, d1 <= static_cast(0), static_cast(sd::DataTypeUtils::min()), d1) // StabilizeX - conditional operation DECLARE_UNARY_CONDITIONAL_OP(StabilizeX, d1 <= static_cast(0), sd::DataTypeUtils::min(), d1) // SoftPlus - simple math function DECLARE_UNARY_MATH_OP(SoftPlus, sd_softplus) // SoftMax - complex math expression using params DECLARE_UNARY_COMPLEX_MATH_OP(SoftMax, sd::math::sd_exp(d1 - params[0]) / params[1]) // LogSoftMax - simple math expression using params DECLARE_UNARY_SIMPLE_OP(LogSoftMax, (d1 - params[0]) - params[1]) // Sech - simple math expression (reciprocal of cosh) DECLARE_UNARY_SIMPLE_OP(Sech, static_cast(1) / sd::math::sd_cosh(d1)) // Csch - simple math expression (reciprocal of sinh) DECLARE_UNARY_SIMPLE_OP(Csch, static_cast(1) / sd::math::sd_sinh(d1)) // Coth - simple math expression (cosh/sinh) DECLARE_UNARY_SIMPLE_OP(Coth, sd::math::sd_cosh(d1) / sd::math::sd_sinh(d1)) DECLARE_UNARY_COMPLEX_CONDITIONAL_OP(ClipByValue, d1 > params[1], params[1], d1 < params[0], params[0], d1) template class LGamma { public: no_op_exec_special_same no_op_exec_special_same_cuda SD_HOST_DEVICE SD_INLINE static X op(X d1, X *params) { return sd::math::sd_lgamma(d1); } }; template class SetRange { private: static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X *params) { auto min = params[0]; auto max = params[1]; if (static_cast(d1) >= min && static_cast(d1) <= max) return d1; if (min == static_cast(0) && max == static_cast(1)) { auto val = static_cast(1) / (static_cast(1) + sd::math::sd_exp(-d1)); return (sd::math::sd_floor(val * (max - min)) + min); } return (sd::math::sd_floor(d1 * (max - min)) + min); } static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X *params) { return op_logic(d1, params); } public: no_op_exec_special_same no_op_exec_special_same_cuda; static SD_HOST_DEVICE SD_INLINE X op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; DECLARE_UNARY_SIMPLE_OP(Affine, params[0] * d1 + params[1]) template class Stabilize { private: static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X *params) { X k = params[0]; if (d1 * k > static_cast(SD_MAX_CUTFOFF)) return static_cast(SD_MAX_CUTFOFF) / k; else if (d1 * k < static_cast(SD_MIN_CUTFOFF)) return static_cast(SD_MIN_CUTFOFF) / k; return d1; } static X op_simd(X d1, X *params) { return op_logic(d1, params); } public: no_op_exec_special_same no_op_exec_special_same_cuda; static SD_HOST_DEVICE SD_INLINE X op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; DECLARE_UNARY_SIMPLE_OP(Ones, static_cast(1.0f)) DECLARE_REDUCE3_OP_WITH_BOOL_SUPPORT(JaccardDistance, Y num_val = static_cast(static_cast(d1) & static_cast(d2)); \ Y denom_val = static_cast(static_cast(d1) | static_cast(d2)); \ extraParamsRef[0] += num_val; \ extraParamsRef[1] += denom_val; \ return static_cast(0.0f);, Y num_val = static_cast(sd::math::sd_min(d1 COMMA d2)); \ Y denom_val = static_cast(sd::math::sd_max(d1 COMMA d2)); \ extraParamsRef[0] += num_val; \ extraParamsRef[1] += denom_val; \ return static_cast(0.0f);, 2, 0.0f, (static_cast(1.0f)) - (extraParamsRef[0] / extraParamsRef[1]) ) DECLARE_HAMMING_DISTANCE_OP_WITH_BOOL_SUPPORT(SimpleHammingDistance, (static_cast(d1) == static_cast(d2)) ? 0.0f : 1.0f, (d1 == d2) ? 0.0f : 1.0f, 0.0f ) DECLARE_REDUCE3_OP_WITH_BOOL_SUPPORT(CosineDistance, extraParamsRef[0] += static_cast(static_cast(d1) * static_cast(d1)); \ extraParamsRef[1] += static_cast(static_cast(d2) * static_cast(d2)); \ return static_cast(static_cast(d1) * static_cast(d2));, extraParamsRef[0] += static_cast(sd::math::sd_abs(d1) * sd::math::sd_abs(d1)); \ extraParamsRef[1] += static_cast(sd::math::sd_abs(d2) * sd::math::sd_abs(d2)); \ return static_cast(d1 * d2);, 2, 0.0f, (static_cast(1.0f)) - (reduction / (sd::math::sd_sqrt(extraParamsRef[0]) * sd::math::sd_sqrt(extraParamsRef[1]))) ) DECLARE_DISTANCE_OP_WITH_BOOL_SUPPORT(Dot, static_cast(d1) * static_cast(d2), d1 * d2, 0.0f ) DECLARE_BOOLEAN_OP_WITH_TYPE_SAFETY(EqualsWithEps, sd::math::sd_eq(d1 COMMA d2 COMMA eps), 1.0f ) DECLARE_DISTANCE_OP_WITH_BOOL_SUPPORT(EuclideanDistance, static_cast(d1) != static_cast(d2) ? 1 : 0, (d1 - d2) * (d1 - d2), 0.0f ) DECLARE_DISTANCE_OP_WITH_BOOL_SUPPORT(ManhattanDistance, static_cast(d1) != static_cast(d2) ? 1 : 0, sd::math::sd_abs(d1 - d2), 0.0f ) template class DropOut { public: no_op_exec_special_same no_op_exec_special_same_cuda SD_HOST_DEVICE SD_INLINE static X op(X d1, X *params) { X prob = params[0]; #ifdef __CUDACC__ X length = params[1]; X tid = blockIdx.x * blockDim.x + threadIdx.x; X rnd = sd::math::sd_abs(sd::math::sd_cos( static_cast(tid) + static_cast(length) * static_cast(tid))); #else X rnd = static_cast(rand() / RAND_MAX); #endif return rnd >= prob ? static_cast(0.0f) : d1; } }; template class DropOutInverted { public: no_op_exec_special no_op_exec_special_cuda #ifdef __CUDACC__ SD_DEVICE #endif SD_HOST_DEVICE SD_INLINE Z op(X d1, Y d2, Z *params) { Y prob = d2; #ifdef __CUDACC__ X length = params[1]; X tid = blockIdx.x * blockDim.x + threadIdx.x; X rnd = sd::math::sd_abs(sd::math::sd_cos( static_cast(tid) + static_cast(length) * static_cast(tid))); #else X rnd = static_cast(rand() / RAND_MAX); #endif return rnd >= static_cast(prob) ? static_cast(0.0f) : reinterpret_cast(d1 / static_cast(prob)); } }; DECLARE_BINARY_COPY_OP(ReplaceNans, sd::math::sd_isnan(d1) ? static_cast(d2) : static_cast(d1), sd::math::sd_isnan(d1) ? static_cast(d2) : static_cast(d1), static_cast(d1), static_cast(d1) ) template class CompareAndReplace { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2, Z *params) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { // All the arithmetic logic is now inside the else block // This code is only compiled when none of X, Y, Z are string types auto zd1 = static_cast(d1); auto zd2 = static_cast(d2); auto compare = params[0]; auto eps = params[2]; int mode = (int)params[3]; // This line is now safe - only compiled for non-string types if (mode == 0) // equals if (sd::math::sd_abs(zd1 - compare) <= eps) return zd2; else return zd1; else if (mode == 1) // not equals eps if (sd::math::sd_abs(zd1 - compare) > eps) return zd2; else return zd1; else if (mode == 2) // less_than eps if (zd1 < compare) return zd2; else return zd1; else if (mode == 3) // greater_than if (zd1 > compare) return zd2; else return zd1; else if (mode == 4) // less_or_equals_than if (zd1 <= compare) return zd2; else return zd1; else if (mode == 5) // greater_or_equals_than if (zd1 >= compare) return zd2; else return zd1; else if (mode == 6) // abs_less_than if (sd::math::sd_abs(zd1) < compare) return zd2; else return zd1; else if (mode == 7) // abs_greater_than if (sd::math::sd_abs(zd1) > compare) return zd2; else return zd1; else if (mode == 8 || mode == 15) // is inf if constexpr (std::is_arithmetic::value) { if (sd::math::sd_isinf(d1)) // Use original d1, not cast zd1 return zd2; else return zd1; } else { return zd1; } else if (mode == 9) // is nan if constexpr (std::is_arithmetic::value) { if (sd::math::sd_isnan(d1)) // Use original d1, not cast zd1 return zd2; else return zd1; } else { return zd1; } else if (mode == 10) if (zd1 == compare) return zd2; else return zd1; else if (mode == 11) if (zd1 != compare) return zd2; else return zd1; else if (mode == 12) // abs_greater_or_equals_than if (sd::math::sd_abs(zd1) >= compare) return zd2; else return zd1; else if (mode == 13) { // abs_less_or_equals_than if (sd::math::sd_abs(zd1) <= compare) return zd2; else return zd1; } else if (mode == 14) { // is_finite (not inf) if constexpr (std::is_arithmetic::value) { if (!sd::math::sd_isinf(d1)) // Use original d1, not cast zd1 return zd2; else return zd1; } else { return zd1; } } else sd_printf("Undefined boolean operation: [%i]\n", mode); return zd1; } } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, Y d2, Z *params) { return op_logic(d1, d2, params); } public: static SD_HOST_DEVICE SD_INLINE Z op(X d1, Y d2, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } }; template class CompareAndSet { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X dX, Y dY, Z *params) { // Type guard for non-arithmetic types - early return if constexpr (any_my_string_v) { return static_cast(dX); // For non-arithmetic types, just return cast of first value } else { // All arithmetic logic inside else block - only compiled for non-string types auto d1 = static_cast(dX); auto d2 = static_cast(dY); auto compare = params[0]; auto eps = params[2]; auto mode = static_cast(params[3]); // Safe - only compiled for non-string types if (mode == 0) // equals if (sd::math::sd_abs(d2 - compare) <= eps) return d2; else return d1; else if (mode == 1) // not equals if (sd::math::sd_abs(d2 - compare) > eps) return d2; else return d1; else if (mode == 2) // less_than if (d2 < compare) return d2; else return d1; else if (mode == 3) // greater_than if (d2 > compare) return d2; else return d1; else if (mode == 4) // less_or_equals_than if (d2 <= compare) return d2; else return d1; else if (mode == 5) // greater_or_equals_than if (d2 >= compare) return d2; else return d1; else if (mode == 6) // abs_less_than if (sd::math::sd_abs(d2) < compare) return d2; else return d1; else if (mode == 7) // abs_greater_than if (sd::math::sd_abs(d2) > compare) return d2; else return d1; else if (mode == 8 || mode == 15) { // is inf if constexpr (std::is_arithmetic::value) { if (sd::math::sd_isinf(dY)) // Use original dY, not cast d2 return d2; else return d1; } else { return d1; } } else if (mode == 9) { // is nan if constexpr (std::is_arithmetic::value) { if (sd::math::sd_isnan(dY)) // Use original dY, not cast d2 return d2; else return d1; } else { return d1; } } else if (mode == 10) if (d2 == compare) return d2; else return d1; else if (mode == 11) if (d2 != compare) return d2; else return d1; else if (mode == 12) // abs_greater_or_equals_than if (sd::math::sd_abs(d1) >= compare) return d2; else return d1; else if (mode == 13) // abs_less_or_equals_than if (sd::math::sd_abs(d1) <= compare) return d2; else return d1; else if (mode == 14) { // is_finite (not inf) if constexpr (std::is_arithmetic::value) { if (!sd::math::sd_isinf(dX)) // Use original dX, not cast d1 return d2; else return d1; } else { return d1; } } else sd_printf("Undefined boolean operation: [%i]\n", mode); return d1; } } static SD_HOST_DEVICE SD_INLINE Z op_simd(X dX, Y dY, Z *params) { return op_logic(dX, dY, params); } public: static SD_HOST_DEVICE SD_INLINE Z op(X dX, Y dY, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(dX, dY, params); else return op_simd(dX, dY, params); } }; template class CompareAndSetTransform { private: static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X *params) { // Type guard for non-arithmetic types - early return if constexpr (!std::is_arithmetic::value) { return d1; // For non-arithmetic types, just return the input value } else { // All arithmetic logic inside else block - only compiled for arithmetic types auto compare = params[0]; auto set = params[1]; auto eps = params[2]; int mode = (int)params[3]; // Safe - only compiled for arithmetic types if (mode == 0) // equals if (sd::math::sd_abs(d1 - compare) <= eps) return set; else return d1; else if (mode == 1) // not equals if (sd::math::sd_abs(d1 - compare) > eps) return set; else return d1; else if (mode == 2) // less_than if (d1 < compare) return set; else return d1; else if (mode == 3) // greater_than if (d1 > compare) return set; else return d1; else if (mode == 4) // less_or_equals_than if (d1 <= compare) return set; else return d1; else if (mode == 5) // greater_or_equals_than if (d1 >= compare) return set; else return d1; else if (mode == 6) // abs_less_than if (sd::math::sd_abs(d1) < compare) return set; else return d1; else if (mode == 7) // abs_greater_than if (sd::math::sd_abs(d1) > compare) return set; else return d1; else if (mode == 8) // is inf if (sd::math::sd_isinf(d1)) return set; else return d1; else if (mode == 9) // is nan if (sd::math::sd_isnan(d1)) return set; else return d1; else if (mode == 10) if (d1 == compare) return set; else return d1; else if (mode == 11) if (d1 != compare) return set; else return d1; else if (mode == 12) // abs_greater_or_equals_than if (sd::math::sd_abs(d1) >= compare) return set; else return d1; else if (mode == 13) // abs_less_or_equals_than if (sd::math::sd_abs(d1) <= compare) return set; else return d1; else if (mode == 14) { // is_finite (not inf) if (!sd::math::sd_isinf(d1)) return compare; // Note: original code returns compare, not set else return d1; } else sd_printf("Undefined boolean operation: [%i]\n", mode); return d1; } } static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X *params) { return op_logic(d1, params); } public: no_op_exec_special_same no_op_exec_special_same_cuda; static SD_HOST_DEVICE SD_INLINE X op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; DECLARE_UNARY_SIMD_SAFE_OP(SELUDerivative, return d1 > static_cast(0.f) ? static_cast(SELU_LAMBDA) : static_cast(SELU_ALPHA) * static_cast(SELU_LAMBDA) * sd::math::sd_exp(d1); ) DECLARE_UNARY_SIMD_SAFE_OP(HardSigmoid, return sd::math::sd_min( static_cast(1) COMMA sd::math::sd_max(static_cast(0) COMMA (static_cast(0.2f)) * d1 + static_cast(0.5f))); ) DECLARE_UNARY_SIMD_SAFE_OP(SELU, return d1 > static_cast(0.0f) ? static_cast(SELU_LAMBDA) * static_cast(d1) : static_cast(SELU_LAMBDA) * (static_cast(SELU_ALPHA) * sd::math::sd_exp(d1) - static_cast(SELU_ALPHA)); ) DECLARE_UNARY_SIMD_SAFE_OP(Swish, return d1 * sd::math::sd_sigmoid(d1); ) DECLARE_UNARY_SIMD_SAFE_OP(SwishDerivative, X ex = sd::math::sd_pow(static_cast(M_E) COMMA d1); return (ex * (d1 + ex + static_cast(1.f))) / sd::math::sd_pow((ex + static_cast(1.f)) COMMA static_cast(2.f)); ) DECLARE_UNARY_SIMD_SAFE_OP(Mish, return d1 * sd::math::sd_tanh(sd::math::sd_softplus(d1)); ) DECLARE_UNARY_SIMD_SAFE_OP(MishDerivative, auto ex = sd::math::sd_exp(d1); auto e2x = ex * ex; auto e3x = ex * ex * ex; return (ex * (4 * (d1 + 1) + 4 * e2x + e3x + ex * (4 * d1 + 6))) / sd::math::sd_pow((2 * ex + e2x + 2) COMMA (X)2.f); ) DECLARE_UNARY_SIMD_SAFE_OP(GELU, return d1 * sd::math::sd_sigmoid(static_cast(1.702f) * d1); ) DECLARE_UNARY_SIMD_SAFE_OP(PreciseGELU, auto sp = sd::math::sd_sqrt(static_cast(2) / static_cast(M_PI)); auto xp = d1 + sd::math::sd_pow(static_cast(0.044715) * d1 COMMA static_cast(3)); return (d1 / static_cast(2)) * (static_cast(1) + sd::math::sd_tanh(sp * xp)); ) DECLARE_UNARY_SIMD_SAFE_OP(GELUDerivative, auto x17 = static_cast(1.702f) * d1; auto ep = sd::math::sd_exp(x17); auto one_plus_ep = static_cast(1.f) + ep; return (ep * (one_plus_ep + x17)) / (one_plus_ep * one_plus_ep); ) DECLARE_UNARY_SIMD_SAFE_OP(PreciseGELUDerivative, auto x79 = static_cast(0.797885) * d1; auto temp1 = static_cast(0.0356774) * d1; auto x03 = temp1 * temp1 * temp1; // cube without sd_pow auto x39 = static_cast(0.398942) * d1; auto temp2 = static_cast(0.0535161) * d1; auto x05 = temp2 * temp2 * temp2; // cube without sd_pow auto scz = sd::math::sd_sech(x79 + x03); return static_cast(0.5) + (x39 + x05) * (scz * scz) + static_cast(0.5) * sd::math::sd_tanh(x79 + x03); ) DECLARE_UNARY_SIMD_SAFE_OP(LogSigmoid, return sd::math::sd_log(sd::math::sd_sigmoid(d1)); ) DECLARE_UNARY_SIMD_SAFE_OP(LogSigmoidDerivative, X ex = sd::math::sd_exp(d1); return static_cast(1.f) / (ex + static_cast(1.f)); ) DECLARE_UNARY_MATH_OP(SigmoidDerivative, sd_sigmoidderivative) DECLARE_UNARY_MATH_OP(TanhDerivative, sd_tanhderivative) DECLARE_UNARY_MATH_OP(SinhDerivative, sd_cosh) // sinh derivative is cosh DECLARE_UNARY_MATH_OP(SoftSignDerivative, sd_softsignderivative) DECLARE_MULTI_OP_SIMD_SAFE(And, return d2 + d1;, if (params != nullptr) { auto comp = params[0]; return d1 != comp && d2 != comp ? static_cast(1) : static_cast(0); } else { auto b1 = static_cast(d1); auto b2 = static_cast(d2); return (b1 && b2) ? static_cast(1) : static_cast(0); }, return d1;, return static_cast(119); ) DECLARE_UNARY_SIMD_SAFE_OP(RationalTanh, auto dis = (static_cast(2) / static_cast(3)) * d1; auto tanh = sd::math::sd_sgn(dis) * (static_cast(1) - (static_cast(1) / (static_cast(1) + static_cast(sd::math::sd_abs(dis)) + sd::math::sd_pow(dis COMMA static_cast(2)) + static_cast(1.41645f) * sd::math::sd_pow(dis COMMA static_cast(4))))); return static_cast(1.7159f) * tanh; ) DECLARE_UNARY_SIMD_SAFE_OP(RationalTanhDerivative, auto dis = (static_cast(2.f) / static_cast(3.f)) * d1; auto a = static_cast(1.f) + sd::math::sd_abs(dis) + sd::math::sd_pow(dis COMMA static_cast(2.f)) + static_cast(1.41645f) * sd::math::sd_pow(dis COMMA static_cast(4)); auto tDeriv = (static_cast(1.f) + sd::math::sd_sign(dis) * (static_cast(2.f) * dis + static_cast(4.f) * static_cast(1.41645f) * sd::math::sd_pow(dis COMMA static_cast(3)))) / (a * a); return static_cast(1.7159f) * (static_cast(2.f) / static_cast(3.f)) * tDeriv; ) DECLARE_UNARY_SIMD_SAFE_OP(ScaledTanh, return params[0] * sd::math::sd_tanh(params[1] * d1); ) // RectifiedTanh operation DECLARE_UNARY_SIMD_SAFE_OP(RectifiedTanh, return sd::math::sd_max(static_cast(0) COMMA sd::math::sd_tanh(d1)); ) // ELU operation DECLARE_BINARY_SIMD_SAFE_OP(ELU, return sd::math::sd_elu(d1 COMMA static_cast(d2)); ) // ELUDerivative operation DECLARE_BINARY_SIMD_SAFE_OP(ELUDerivative, return sd::math::sd_eluderivative(d1 COMMA static_cast(d2)); ) // RELU operation DECLARE_BINARY_SIMD_SAFE_OP(RELU, auto xt = static_cast(d1); auto xf = static_cast(d2); return xt < xf ? xf : xt; ) // RELUDerivative operation DECLARE_BINARY_SIMD_SAFE_OP(RELUDerivative, auto xt = static_cast(d1); auto xf = static_cast(d2); return xt > xf ? static_cast(1.f) : static_cast(0.f); ) DECLARE_BINARY_SIMD_SAFE_OP(RELU6, auto relu = RELU::op(d1 COMMA d2 COMMA params); return relu < static_cast(6) ? relu : static_cast(6); ) DECLARE_BINARY_SIMD_SAFE_OP(LeakyRELU, auto val = static_cast(d1); auto alpha = static_cast(d2); return val < 0.0f ? alpha * val : val; ) DECLARE_BINARY_SIMD_SAFE_OP(LeakyRELUDerivative, if (d1 >= static_cast(0)) return static_cast(1); else return static_cast(d2); ) DECLARE_REDUCE_SIMD_SAFE_OP(IsNan, return sd::math::sd_isnan(d1) ? static_cast(1) : static_cast(0); ) DECLARE_REDUCE_SIMD_SAFE_OP(IsPositive, return d1 > (X)0.f; ) DECLARE_REDUCE_SIMD_SAFE_OP(IsNegative, return d1 < (X)0.f; ) DECLARE_REDUCE_SIMD_SAFE_OP(IsInf, return sd::math::sd_isinf(d1) ? static_cast(1) : static_cast(0); ) // IsInfOrNan operation DECLARE_REDUCE_SIMD_SAFE_OP(IsFinite, return sd::math::sd_isfin(d1) ? static_cast(1) : static_cast(0); ) DECLARE_REDUCE_SIMD_SAFE_OP(IsInfOrNan, return sd::math::sd_isfin(d1) ? static_cast(0) : static_cast(1); ) DECLARE_UNARY_SIMPLE_OP(TimesOneMinus, d1 * (static_cast(1) - d1)) DECLARE_UNARY_SIMPLE_OP(CubeDerivative, static_cast(3) * d1 * d1) DECLARE_UNARY_SIMPLE_OP(SpecialDerivative, d1 * (static_cast(1.f) - d1)) DECLARE_COMPLEX_ACCUMULATION_SIMD_SAFE_OP(ASum, return sd::math::sd_abs(d1);, return sd::math::sd_abs(d1) + sd::math::sd_abs(d2);, return sd::math::sd_abs(d1) + sd::math::sd_abs(d2);, ASUM, static_cast(0), sd::math::sd_abs(opOutput) + sd::math::sd_abs(old), sd::math::sd_abs(opOutput) + sd::math::sd_abs(old), sd::math::sd_abs(reduction) ) DECLARE_SIMPLE_REDUCTION_OP( CountNonZero, ASUM, static_cast(0), (d1 == static_cast(0.0f) ? static_cast(0.0f) : static_cast(1.0f)), (opOutput + old), (opOutput + old), static_cast(reduction) ) DECLARE_SIMPLE_REDUCTION_OP( CountZero, SUM, static_cast(0.0f), (d1 == static_cast(0) ? static_cast(1) : static_cast(0)), (opOutput + old), (opOutput + old), static_cast(reduction) ) DECLARE_MIXED_ACCUMULATION_SIMD_SAFE_OP(Any, return d1;, SUM, static_cast(0.0f), opOutput + old, opOutput + old, reduction > static_cast(0) ? static_cast(1) : static_cast(0) ) DECLARE_MIXED_ACCUMULATION_SIMD_SAFE_OP(All, return d1;, SUM, static_cast(1), static_cast(static_cast(opOutput) && static_cast(old) ? 1 : 0), static_cast(static_cast(opOutput) && static_cast(old) ? 1 : 0), reduction > static_cast(0) ? static_cast(1) : static_cast(0) ) // AMean operation DECLARE_ACCUMULATION_SIMD_SAFE_OP(AMean, return static_cast(sd::math::sd_abs(d1));, ASUM, static_cast(0), sd::math::sd_abs(opOutput) + sd::math::sd_abs(old), opOutput + old, static_cast(reduction / static_cast(n)) ) DECLARE_COMPLEX_ACCUMULATION_SIMD_SAFE_OP(AMax, return sd::math::sd_abs(d1);, return sd::math::sd_max(sd::math::sd_abs(d1) COMMA sd::math::sd_abs(d2));, return sd::math::sd_abs(d1) > sd::math::sd_abs(d2) ? d1 : d2;, AMAX, input[0], sd::math::sd_max(sd::math::sd_abs(old) COMMA sd::math::sd_abs(opOutput)), sd::math::sd_max(sd::math::sd_abs(opOutput) COMMA sd::math::sd_abs(old)), sd::math::sd_abs(reduction) ) DECLARE_COMPLEX_ACCUMULATION_SIMD_SAFE_OP(AMin, return sd::math::sd_abs(d1);, return sd::math::sd_min(sd::math::sd_abs(d1) COMMA sd::math::sd_abs(d2));, return sd::math::sd_min(sd::math::sd_abs(d1) COMMA sd::math::sd_abs(d2));, AMIN, input[0], sd::math::sd_min(sd::math::sd_abs(old) COMMA sd::math::sd_abs(opOutput)), sd::math::sd_min(sd::math::sd_abs(opOutput) COMMA sd::math::sd_abs(old)), sd::math::sd_abs(reduction) ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(Norm1, return static_cast(sd::math::sd_abs(d1));, SUM, static_cast(0), opOutput + old, opOutput + old, reduction ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(Norm2, auto v = static_cast(d1); return v * v;, SUM, static_cast(0), opOutput + old, opOutput + old, sd::math::sd_sqrt(reduction) ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(SquaredNorm, auto v = static_cast(d1); return v * v;, SUM, static_cast(0), opOutput + old, opOutput + old, reduction ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(NormFrobenius, auto v = static_cast(sd::math::sd_abs(d1)); return v * v;, SUM, static_cast(0), opOutput + old, opOutput + old, sd::math::sd_sqrt(reduction) ) DECLARE_MIXED_ACCUMULATION_SIMD_SAFE_OP(NormP, return sd::math::sd_pow(static_cast(sd::math::sd_abs(d1)) COMMA static_cast(params[0]));, SUM, static_cast(0), opOutput + old, opOutput + old, sd::math::sd_pow(reduction COMMA static_cast(1.0f) / extraParams[0]) ) DECLARE_MIXED_ACCUMULATION_SIMD_SAFE_OP(NormMax, return static_cast(d1);, SUM, static_cast(0), opOutput + old, sd::math::sd_max(sd::math::sd_abs(old) COMMA sd::math::sd_abs(opOutput)), sd::math::sd_max(sd::math::sd_abs(reduction) COMMA sd::math::sd_abs(reduction)) ) // --- Generic Assign Template --- template class Assign { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X *params) { if constexpr (std::is_same_v) { return d1; // No conversion needed } else if constexpr (std::is_convertible_v) { return static_cast(d1); // Use static_cast for direct convertibility } else { // This will trigger a compile error for unsupported types, // requiring a specialization like the ones below. return static_cast(d1); } } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, X *params) { return op_logic(d1, params); } public: static SD_HOST_DEVICE SD_INLINE Z op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; // --- Specialization: std::basic_string (UTF-16) -> std::basic_string (UTF-8) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES]; const char16_t* input_data = d1.data(); const uint32_t input_length_char16_units = static_cast(d1.length()); sd::LongType required_bytes = sd::unicode::offsetUtf16StringInUtf8(input_data, input_length_char16_units); if (required_bytes > 0 && static_cast(required_bytes) <= SD_STRING_ASSIGN_TEMP_BUFFER_BYTES) { void* end_ptr = sd::unicode::utf16to8Ptr(input_data, input_data + input_length_char16_units, temp_output_buffer); size_t bytes_written = static_cast(end_ptr) - temp_output_buffer; if (bytes_written == static_cast(required_bytes)) { return std::basic_string(temp_output_buffer, bytes_written); } } return std::basic_string(); } }; // --- Specialization: std::basic_string (UTF-8) -> std::basic_string (UTF-16) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char16_t temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES / sizeof(char16_t) + 1]; const char* input_data = d1.data(); const uint32_t input_length_bytes = static_cast(d1.length()); sd::LongType required_bytes_for_utf16 = sd::unicode::offsetUtf8StringInUtf16(input_data, input_length_bytes); if (required_bytes_for_utf16 > 0 && static_cast(required_bytes_for_utf16) < sizeof(temp_output_buffer) ) { void* end_ptr = sd::unicode::utf8to16Ptr(input_data, input_data + input_length_bytes, temp_output_buffer); size_t char16_units_written = static_cast(end_ptr) - temp_output_buffer; if (char16_units_written * sizeof(char16_t) == static_cast(required_bytes_for_utf16)) { return std::basic_string(temp_output_buffer, char16_units_written); } } return std::basic_string(); } }; // --- Specialization: std::basic_string (UTF-32) -> std::basic_string (UTF-8) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, // Z is std::basic_string const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES]; const char32_t* input_data = d1.data(); const uint32_t input_length_char32_units = static_cast(d1.length()); sd::LongType required_bytes = sd::unicode::offsetUtf32StringInUtf8(input_data, input_length_char32_units); if (required_bytes > 0 && static_cast(required_bytes) <= SD_STRING_ASSIGN_TEMP_BUFFER_BYTES) { void* end_ptr = sd::unicode::utf32to8Ptr(input_data, input_data + input_length_char32_units, temp_output_buffer); size_t bytes_written = static_cast(end_ptr) - temp_output_buffer; if (bytes_written == static_cast(required_bytes)) { return std::basic_string(temp_output_buffer, bytes_written); } } return std::basic_string(); } }; // --- Specialization: std::basic_string (UTF-8) -> std::basic_string (UTF-32) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char32_t temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES / sizeof(char32_t) + 1]; const char* input_data = d1.data(); const uint32_t input_length_bytes = static_cast(d1.length()); sd::LongType required_bytes_for_utf32_data = sd::unicode::offsetUtf8StringInUtf32(input_data, input_length_bytes); if (required_bytes_for_utf32_data > 0 && static_cast(required_bytes_for_utf32_data) < sizeof(temp_output_buffer) ) { void* end_ptr = sd::unicode::utf8to32Ptr(input_data, input_data + input_length_bytes, temp_output_buffer); size_t char32_units_written = static_cast(end_ptr) - temp_output_buffer; if (char32_units_written * sizeof(char32_t) == static_cast(required_bytes_for_utf32_data)) { return std::basic_string(temp_output_buffer, char32_units_written); } } return std::basic_string(); } }; // --- Identity Specializations --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { return d1; } }; template class LogPoissonLossFull { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X z, Y c) { auto zz = static_cast(z); auto zc = static_cast(c); return (sd::math::sd_exp(c) - zz * zc + (zz * sd::math::sd_log(z) - zz + static_cast(0.5f) * sd::math::sd_log(static_cast(SD_DOUBLE_PI_X) * zz))); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X z, Y c, Z *params) { return op_logic(z, c); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X z) { auto zz = static_cast(z); return (zz * sd::math::sd_log(z) - zz + static_cast(0.5f) * sd::math::sd_log(static_cast(SD_DOUBLE_PI_X) * zz)); } static SD_HOST_DEVICE SD_INLINE X op_logic(X z, Y *params) { return (sd::math::sd_exp(params[0]) - z * params[0] + (z * sd::math::sd_log(z) - z + static_cast(0.5f) * sd::math::sd_log(SD_DOUBLE_PI_X * z))); } static SD_HOST_DEVICE Z op_simd(X z, Y c) { return op_logic(z, c); } static SD_HOST_DEVICE Z op_simd(X z, Y c, Z *params) { return op_logic(z, c, params); } static SD_HOST_DEVICE Z op_simd(X z) { return op_logic(z); } static SD_HOST_DEVICE X op_simd(X z, Y *params) { return op_logic(z, params); } public: static SD_HOST_DEVICE Z op(X z, Y c) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(z, c); else return op_simd(z, c); } static SD_HOST_DEVICE Z op(X z, Y c, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(z, c, params); else return op_simd(z, c, params); } static SD_HOST_DEVICE Z op(X z) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(z); else return op_simd(z); } static SD_HOST_DEVICE X op(X z, Y *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(z, params); else return op_simd(z, params); } }; template class Celu { private: static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X *params) { X alpha = params[0]; return sd::math::sd_max(static_cast(0), d1) + sd::math::sd_min(static_cast(0), alpha * (sd::math::sd_exp(d1/alpha) - static_cast(1))); } static X op_simd(X d1, X *params) { return op_logic(d1, params); } public: no_op_exec_special_same no_op_exec_special_same_cuda; static SD_HOST_DEVICE X op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; // ELUAlpha - simple conditional with parameter DECLARE_UNARY_CLIPPING_OP(ELUAlpha, X alpha = params[0]; return d1 > static_cast(0) ? d1 : alpha * (sd::math::sd_exp(d1) - static_cast(1)); ) // PReLU - simple conditional with parameter DECLARE_UNARY_CLIPPING_OP(PReLU, X alpha = params[0]; return d1 > static_cast(0) ? d1 : alpha * d1; ) // ThresholdedReLU - simple threshold operation DECLARE_UNARY_CLIPPING_OP(ThresholdedReLU, X theta = params[0]; return d1 > theta ? d1 : static_cast(0); ) #define DECLARE_INTEGER_ONLY_BINARY_OP(OP_NAME, OPERATION) \ template \ class OP_NAME { \ private: \ static SD_INLINE X op_logic(X d1, X d2) { \ if constexpr (std::is_integral::value) { \ return OPERATION; \ } else { \ /* For floating point, interpret bits as integer */ \ using IntType = typename std::conditional::type>::type>::type;\ IntType i1, i2, iresult; \ std::memcpy(&i1, &d1, sizeof(X)); \ std::memcpy(&i2, &d2, sizeof(X)); \ /* Create a lambda to evaluate OPERATION with integer values */ \ iresult = [&]() { \ auto d1 = i1; \ auto d2 = i2; \ return OPERATION; \ }(); \ X result; \ std::memcpy(&result, &iresult, sizeof(X)); \ return result; \ } \ } \ static SD_INLINE X op_logic(X d1, X d2, X *params) { return op_logic(d1, d2); } \ static SD_INLINE SD_HOST_DEVICE X op_simd(X d1, X d2) { return op_logic(d1, d2); } \ static SD_INLINE SD_HOST_DEVICE X op_simd(X d1, X d2, X *params) { return op_logic(d1, d2, params); } \ \ public: \ static SD_INLINE SD_HOST_DEVICE X op(X d1, X d2) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2); \ else \ return op_simd(d1, d2); \ } \ static SD_INLINE SD_HOST_DEVICE X op(X d1, X d2, X *params) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2, params); \ else \ return op_simd(d1, d2, params); \ } \ }; // Then use it exactly as before: DECLARE_INTEGER_ONLY_BINARY_OP(ShiftLeft, d1 << d2) DECLARE_INTEGER_ONLY_BINARY_OP(ShiftRight, d1 >> d2) DECLARE_INTEGER_ONLY_BINARY_OP(IntOr, d2 | d1) DECLARE_INTEGER_ONLY_BINARY_OP(IntAnd, d2 & d1) DECLARE_INTEGER_ONLY_BINARY_OP(IntXor, d2 ^ d1) #define DECLARE_INTEGER_ONLY_BINARY_TEMPLATE_OP(OP_NAME, OPERATION) \ template \ class OP_NAME { \ private: \ static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X d2) { \ if constexpr (std::is_integral::value) { \ return OPERATION; \ } else { \ /* For floating point types, the template operation might not be valid */ \ /* This requires special handling based on the specific operation */ \ return static_cast(0); /* Default fallback - you may need to specialize per operation */ \ } \ } \ static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X d2, X *params) { return op_logic(d1, d2); } \ static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X d2) { return op_logic(d1, d2); } \ static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X d2, X *params) { return op_logic(d1, d2, params); } \ \ public: \ static SD_HOST_DEVICE SD_INLINE X op(X d1, X d2) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2); \ else \ return op_simd(d1, d2); \ } \ static SD_HOST_DEVICE SD_INLINE X op(X d1, X d2, X *params) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2, params); \ else \ return op_simd(d1, d2, params); \ } \ }; // Then use it for cyclic shift operations: DECLARE_INTEGER_ONLY_BINARY_TEMPLATE_OP(CyclicShiftLeft, sd::math::sd_rotl(d1, d2)) DECLARE_INTEGER_ONLY_BINARY_TEMPLATE_OP(CyclicShiftRight, sd::math::sd_rotr(d1, d2)) // Undefine before redefining with improved SFINAE-based constraint #undef DECLARE_INTEGER_ONLY_BINARY_TEMPLATE_OP #define DECLARE_INTEGER_ONLY_BINARY_TEMPLATE_OP(OP_NAME, OPERATION) \ template ::value>::type> \ class OP_NAME { \ private: \ static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X d2) { return OPERATION; } \ static SD_HOST_DEVICE SD_INLINE X op_logic(X d1, X d2, X *params) { return op_logic(d1, d2); } \ static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X d2) { return op_logic(d1, d2); } \ static SD_HOST_DEVICE SD_INLINE X op_simd(X d1, X d2, X *params) { return op_logic(d1, d2, params); } \ \ public: \ static SD_HOST_DEVICE SD_INLINE X op(X d1, X d2) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2); \ else \ return op_simd(d1, d2); \ } \ static SD_HOST_DEVICE SD_INLINE X op(X d1, X d2, X *params) { \ if constexpr (simdOps::is_simd_unsupported_return_type::value) \ return op_logic(d1, d2, params); \ else \ return op_simd(d1, d2, params); \ } \ }; template class Mod { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2) { auto dx = static_cast(d2); auto f = sd::math::sd_floor(d1 / dx); auto r = f * dx; return static_cast(d1 - r); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2, Z *params) { return op_logic(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y *params) { return op_logic(d1, params[0]); } static SD_HOST_DEVICE Z op_simd(X d1, Y d2) { return op_logic(d1, d2); } static SD_HOST_DEVICE Z op_simd(X d1, Y d2, Z *params) { return op_logic(d1, d2, params); } static SD_HOST_DEVICE Z op_simd(X d1, Y *params) { return op_logic(d1, params); } public: static SD_HOST_DEVICE Z op(X d1, Y d2) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2); else return op_simd(d1, d2); } static SD_HOST_DEVICE Z op(X d1, Y d2, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } static SD_HOST_DEVICE Z op(X d1, Y *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; DECLARE_BINARY_COPY_OP(ReverseMod, static_cast(static_cast(d2) % static_cast(d1)), static_cast(static_cast(d2) % static_cast(d1)), static_cast(d1), static_cast(static_cast(params[0]) % static_cast(d1)) ) template class Epsilon { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X d2) { X diff = d1 - d2; X absDiff = sd::math::sd_abs(diff); if (absDiff <= static_cast(SD_MIN_V)) return static_cast(1); return static_cast(0); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X d2, X *params) { X diff = d1 - d2; X absDiff = sd::math::sd_abs(diff); if(params != nullptr && absDiff <= static_cast(params[0])) { return static_cast(1); } else if(absDiff <= static_cast(1e-5)) { return static_cast(1); } return static_cast(0); } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X *params) { return static_cast(d1); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, X d2) { return op_logic(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, X d2, X *params) { return op_logic(d1, d2, params); } static SD_HOST_DEVICE SD_INLINE Z op_simd(X d1, X *params) { return op_logic(d1, params); } public: static SD_HOST_DEVICE SD_INLINE Z op(X d1, X d2) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2); else return op_simd(d1, d2); } static SD_HOST_DEVICE SD_INLINE Z op(X d1, X d2, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } static SD_HOST_DEVICE SD_INLINE Z op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, params); else return op_simd(d1, params); } }; template class MatchConditionBool { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X *extraParams) { X compare = extraParams[0]; X eps = extraParams[1]; auto mode = static_cast(extraParams[2]); sd_debug("value: %f; comp: %f; eps: %f; mode: %i;\n", d1, compare, eps, mode); switch (mode) { case 0: // equals return sd::math::sd_abs(d1 - compare) <= eps ? static_cast(1) : static_cast(0); case 1: // not equals return sd::math::sd_abs(d1 - compare) > eps ? static_cast(1) : static_cast(0); case 2: // less_than return d1 < compare ? static_cast(1) : static_cast(0); case 3: // greater_than return d1 > compare ? static_cast(1) : static_cast(0); case 4: // less_or_equals_than return d1 <= compare ? static_cast(1) : static_cast(0); case 5: // greater_or_equals_than return d1 >= compare ? static_cast(1) : static_cast(0); case 6: // abs_less_than return sd::math::sd_abs(d1) < compare ? static_cast(1) : static_cast(0); case 7: // abs_greater_than return sd::math::sd_abs(d1) > compare ? static_cast(1) : static_cast(0); case 8: // is inf return sd::math::sd_isinf(d1) ? static_cast(1) : static_cast(0); case 9: // is nan return sd::math::sd_isnan(d1) ? static_cast(1) : static_cast(0); case 10: return (d1 == compare) ? static_cast(1) : static_cast(0); case 11: return (d1 != compare) ? static_cast(1) : static_cast(0); case 12: // abs_greater_or_equals_than return sd::math::sd_abs(d1) >= compare ? static_cast(1) : static_cast(0); case 13: // abs_less_or_equals_than return sd::math::sd_abs(d1) <= compare ? static_cast(1) : static_cast(0); case 14: // isFinite return !(sd::math::sd_isinf(d1) || sd::math::sd_isnan(d1)) ? static_cast(1) : static_cast(0); case 15: // isInfinite return (sd::math::sd_isinf(d1) || sd::math::sd_isnan(d1)) ? static_cast(1) : static_cast(0); default: sd_debug("Undefined match condition: [%i]\n", mode); } return static_cast(d1); } // Remove SD_OP_DEF to avoid SIMD issues with float16/bfloat16 static Z op_simd(X d1, X *extraParams) { return op_logic(d1, extraParams); } public: // Fix: Use explicit declarations instead of problematic macros no_op_exec_special no_op_exec_special_cuda; // Special handling for bool operations with type compatibility static const bool requiresSpecialAccumulation = false; // Primary execSpecial function with Z_TYPE* extraParams (for boolean case) static void execSpecial(const X *x, const sd::LongType *xShapeInfo, Z *extraParams, Z *result, const sd::LongType *resultShapeInfoBuffer, sd::LongType *dimension, sd::LongType dimensionLength, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffset) {} // Template overload to handle type conversions (for cases where extraParams is sd::LongType*) template static void execSpecial(const X *x, const sd::LongType *xShapeInfo, ExtraParamsType *extraParams, Z *result, const sd::LongType *resultShapeInfoBuffer, sd::LongType *dimension, sd::LongType dimensionLength, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffset) { // Handle type conversion if needed - this handles the sd::LongType* to Z* conversion // For most cases, this will be empty since we don't actually implement special accumulation } #ifdef __CUDACC__ static SD_INLINE SD_DEVICE void execSpecialCuda( const X *dx, const sd::LongType *xShapeInfo, Z *extraParams, Z *result, const sd::LongType *resultShapeInfo, sd::LongType *dimension, sd::LongType dimensionLength, Z *reductionBuffer, const sd::LongType *tadOnlyShapeInfo, const sd::LongType *tadOffsets) {} template static SD_INLINE SD_DEVICE void execSpecialCuda( const X *dx, const sd::LongType *xShapeInfo, ExtraParamsType *extraParams, Z *result, const sd::LongType *resultShapeInfo, sd::LongType *dimension, sd::LongType dimensionLength, Z *reductionBuffer, const sd::LongType *tadOnlyShapeInfo, const sd::LongType *tadOffsets) {} #endif static SD_HOST_DEVICE SD_INLINE Z op(X d1, X *extraParams) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, extraParams); else return op_simd(d1, extraParams); } }; DECLARE_MULTI_OP_SIMD_SAFE(Or, return d2 + d1;, if (params != nullptr) { auto comp = params[0]; return d1 != comp || d2 != comp ? static_cast(1) : static_cast(0); } else { auto b1 = static_cast(d1); auto b2 = static_cast(d2); return b1 || b2 ? static_cast(1) : static_cast(0); }, return d1;, return static_cast(119); ) DECLARE_XOR_SIMD_SAFE(Xor, return d2 + d1;, if (params != nullptr) { auto comp = params[0]; return ((d1 == comp && d2 != comp) || (d1 != comp && d2 == comp)) ? static_cast(1) : static_cast(0); } else { auto b1 = static_cast(d1); auto b2 = static_cast(d2); return (!b1 && b2) || (b1 && !b2) ? static_cast(1) : static_cast(0); }, return d1; ) DECLARE_NOT_SIMD_SAFE(Not, return static_cast(0);, return d1 != d2 ? static_cast(1) : static_cast(0);, auto b1 = static_cast(d1); return !b1; ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(Variance, X mean = static_cast(params[0]); X ret = d1 - mean; return ret * ret;, SUM, static_cast(0.0f), old + opOutput, old + opOutput, static_cast(reduction / static_cast(n - 1)) ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(StandardDeviation, InterType mean = static_cast(params[0]); InterType ret = d1 - mean; return ret * ret;, SUM, static_cast(0.0f), old + opOutput, old + opOutput, sd::math::sd_sqrt(static_cast(reduction / static_cast(n - 1))) ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(ShannonEntropy, auto p = d1; return static_cast(p) * sd::math::sd_log2(p);, SUM, static_cast(0), opOutput + old, opOutput + old, -reduction ) DECLARE_ACCUMULATION_SIMD_SAFE_OP(LogEntropy, return static_cast(d1) * sd::math::sd_log(d1);, SUM, static_cast(0), opOutput + old, opOutput + old, sd::math::sd_log(-reduction) ) template class IndexAbsoluteMax { public: static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue val, X *extraParams) { return sd::math::sd_abs(val); } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue update( functions::indexreduce::IndexValue &old, functions::indexreduce::IndexValue &opOutput, X *extraParams) { opOutput.value = sd::math::sd_abs(opOutput.value); old.value = sd::math::sd_abs(old.value); if (opOutput.value > old.value) return opOutput; #ifdef __CUDACC__ else if (opOutput.value == old.value && opOutput.index < old.index) return opOutput; #endif return old; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue merge(functions::indexreduce::IndexValue f1, functions::indexreduce::IndexValue f2, X *extraParams) { if (sd::math::sd_abs(f1.value) > sd::math::sd_abs(f2.value)) return f2; return f1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue postProcess( functions::indexreduce::IndexValue reduction, int n, int xOffset, X *dx, int incx, X *extraParams, X *result) { return reduction; } static SD_HOST_DEVICE SD_INLINE X startingValue(const X *input) { return static_cast(0); } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue startingIndexValue(const X *input) { functions::indexreduce::IndexValue local; local.value = startingValue(input); local.index = 0; return local; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue d1, functions::indexreduce::IndexValue d2, X *extraParams) { return d1; } }; template class IndexAbsoluteMin { public: static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue val, X *extraParams) { return val; } static SD_HOST_DEVICE SD_INLINE X startingValue(const X *input) { return sd::DataTypeUtils::infOrMax(); } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue startingIndexValue(const X *input) { functions::indexreduce::IndexValue local; local.value = startingValue(input); local.index = 0; return local; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue update( functions::indexreduce::IndexValue &old, functions::indexreduce::IndexValue &opOutput, X *extraParams) { opOutput.value = sd::math::sd_abs(opOutput.value); old.value = sd::math::sd_abs(old.value); if (opOutput.value < old.value) return opOutput; #ifdef __CUDACC__ else if (opOutput.value == old.value && opOutput.index < old.index) return opOutput; #endif return old; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue merge(functions::indexreduce::IndexValue f1, functions::indexreduce::IndexValue f2, X *extraParams) { if (sd::math::sd_abs(f1.value) < sd::math::sd_abs(f2.value)) return f2; return f1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue postProcess( functions::indexreduce::IndexValue reduction, int n, int xOffset, X *dx, int incx, X *extraParams, X *result) { return reduction; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue d1, functions::indexreduce::IndexValue d2, X *extraParams) { return d1; } }; template class FirstIndex { public: static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue val, X *extraParams) { return val; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue update(functions::indexreduce::IndexValue &old, functions::indexreduce::IndexValue &opOutput, X *extraParams) { #ifdef __CUDACC__ if (opOutput.index < 0) return old; #endif auto res = MatchConditionBool::op(opOutput.value, extraParams); if (res == static_cast(0)) return old; if (old.index < 0) return opOutput; if (old.index > opOutput.index) return opOutput; return old; } static SD_HOST_DEVICE SD_INLINE X startingValue(const X *input) { return -sd::DataTypeUtils::infOrMax(); } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue startingIndexValue(const X *input) { functions::indexreduce::IndexValue local; local.value = startingValue(input); local.index = -1; return local; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue d1, functions::indexreduce::IndexValue d2, X *extraParams) { return d1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue merge(functions::indexreduce::IndexValue f1, functions::indexreduce::IndexValue f2, X *extraParams) { if (f1.index > f2.index) return f2; return f1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue postProcess( functions::indexreduce::IndexValue reduction, int n, int xOffset, X *dx, int incx, X *extraParams, X *result) { return reduction; } }; template class LastIndex { public: static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue val, X *extraParams) { return val; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue update(functions::indexreduce::IndexValue &old, functions::indexreduce::IndexValue &opOutput, X *extraParams) { #ifdef __CUDACC__ if (opOutput.index < 0) return old; #endif auto res = MatchConditionBool::op(opOutput.value, extraParams); if (res == static_cast(0)) return old; if (old.index < 0) return opOutput; if (old.index < opOutput.index) return opOutput; return old; } static SD_HOST_DEVICE SD_INLINE X startingValue(const X *input) { return -sd::DataTypeUtils::infOrMax(); } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue startingIndexValue(const X *input) { functions::indexreduce::IndexValue local; local.value = startingValue(input); local.index = -1; return local; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue op(functions::indexreduce::IndexValue d1, functions::indexreduce::IndexValue d2, X *extraParams) { return d1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue merge(functions::indexreduce::IndexValue f1, functions::indexreduce::IndexValue f2, X *extraParams) { if (f1.index < f2.index) return f2; return f1; } static SD_HOST_DEVICE SD_INLINE functions::indexreduce::IndexValue postProcess( functions::indexreduce::IndexValue reduction, int n, int xOffset, X *dx, int incx, X *extraParams, X *result) { return reduction; } }; DECLARE_ACCUMULATION_SIMD_SAFE_OP(Entropy, return static_cast(d1) * sd::math::sd_log(d1);, SUM, static_cast(0), opOutput + old, opOutput + old, static_cast(-reduction) ) template class SummaryStatsVariance { public: static SD_HOST_DEVICE SD_INLINE Z getValue(const bool biasCorrected, functions::summarystats::SummaryStatsData val) { if (biasCorrected) { Z ret = static_cast(val.varianceBiasCorrected()); if (ret < static_cast(0.0f)) return static_cast(val.variance()); return ret; } return static_cast(val.variance()); } static SD_HOST_DEVICE SD_INLINE functions::summarystats::SummaryStatsData op( functions::summarystats::SummaryStatsData d1, Z *extraParams) { return d1; } }; template class SummaryStatsStandardDeviation { public: static SD_HOST_DEVICE SD_INLINE Z getValue(const bool biasCorrected, functions::summarystats::SummaryStatsData val) { if (biasCorrected) { auto ret = static_cast(val.varianceBiasCorrected()); if (ret < static_cast(0.0f)) return sd::math::sd_sqrt(val.variance()); else return sd::math::sd_sqrt(ret); } return sd::math::sd_sqrt(val.variance()); } static SD_HOST_DEVICE SD_INLINE functions::summarystats::SummaryStatsData op( functions::summarystats::SummaryStatsData d1, Z *extraParams) { return d1; } }; // ============================================================================= // MISSING ADDITIONAL UNARY OPERATIONS // ============================================================================= DECLARE_UNARY_SIMD_SAFE_OP(Sqr, return sd::math::sd_pow(d1 COMMA static_cast(2)); ) DECLARE_UNARY_MATH_OP_XZ(Sqrt, sd_sqrt) // For RSqrt, use the complex math version: DECLARE_UNARY_COMPLEX_MATH_OP_XZ(RSqrt, static_cast(1.0) / static_cast(sd::math::sd_sqrt(d1))) DECLARE_BINARY_COPY_OP(RelativeError, static_cast(sd::math::sd_re(d1, static_cast(d2))), static_cast(sd::math::sd_re(d1, static_cast(d2))), static_cast(0), static_cast(0) ) template class BinaryRelativeError { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2, Z *params) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { // All arithmetic operations inside else block X threshold = static_cast(params[0]); return sd::math::sd_re(d1, static_cast(d2)) > threshold ? static_cast(1) : static_cast(0); } } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { return static_cast(0); } } SD_HOST_DEVICE SD_INLINE static Z op_simd(X d1, Y d2, Z *params) { return op_logic(d1, d2, params); } SD_HOST_DEVICE SD_INLINE static Z op_simd(X d1) { return op_logic(d1); } public: no_op_exec_special no_op_exec_special_cuda static SD_HOST_DEVICE Z op(X d1, Y d2, Z *params) { if constexpr (any_my_string_v || simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } static SD_HOST_DEVICE Z op(X d1) { if constexpr (any_my_string_v || simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1); else return op_simd(d1); } }; // BinaryRelativeError - Custom conditional logic, manual implementation needed template class BinaryMinimumAbsoluteRelativeError { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X *params) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { // All arithmetic operations inside else block X d2 = params[0]; X thresholdRelative = params[1]; X thresholdAbsolute = params[2]; return sd::math::sd_re(d1, d2) > thresholdRelative ? (sd::math::sd_abs(d1 - static_cast(d2)) < thresholdAbsolute ? static_cast(0) : static_cast(1)) : static_cast(0); } } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, Y d2, Z *params) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { // All arithmetic operations inside else block X thresholdRelative = static_cast(params[0]); X thresholdAbsolute = static_cast(params[1]); return sd::math::sd_re(d1, static_cast(d2)) > thresholdRelative ? (sd::math::sd_abs(d1 - static_cast(d2)) < thresholdAbsolute ? static_cast(0) : static_cast(1)) : static_cast(0); } } static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1) { // Type guard for non-arithmetic types if constexpr (any_my_string_v) { return static_cast(d1); // For non-arithmetic types, just return cast of first value } else { return static_cast(0); } } SD_HOST_DEVICE SD_INLINE static Z op_simd(X d1, X *params) { return op_logic(d1, params); } SD_HOST_DEVICE SD_INLINE static Z op_simd(X d1, Y d2, Z *params) { return op_logic(d1, d2, params); } SD_HOST_DEVICE SD_INLINE static Z op_simd(X d1) { return op_logic(d1); } public: no_op_exec_special no_op_exec_special_cuda static SD_HOST_DEVICE SD_INLINE Z op(X d1, X *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, params); else return op_simd(d1, params); } static SD_HOST_DEVICE SD_INLINE Z op(X d1, Y d2, Z *params) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1, d2, params); else return op_simd(d1, d2, params); } static SD_HOST_DEVICE SD_INLINE Z op(X d1) { if constexpr (simdOps::is_simd_unsupported_return_type::value || simdOps::is_simd_unsupported_argument_type::value) return op_logic(d1); else return op_simd(d1); } }; // ============================================================================= // REDUCE OPERATIONS // ============================================================================= DECLARE_REDUCE_OP(Sum, SUM, static_cast(0.0f), opOutput + old, opOutput + old, reduction) DECLARE_REDUCE_OP(Prod, PRODUCT, static_cast(1), opOutput * old, opOutput * old, reduction) DECLARE_REDUCE_OP(Max, MAX, -sd::DataTypeUtils::infOrMax(), sd::math::sd_max(old, opOutput), sd::math::sd_max(opOutput, old), reduction) DECLARE_REDUCE_OP(Min, MIN, sd::DataTypeUtils::infOrMax(), sd::math::sd_min(old, opOutput), sd::math::sd_min(opOutput, old), reduction) DECLARE_ACCUMULATION_SIMD_SAFE_OP(Mean, return static_cast(d1);, SUM, static_cast(0), old + opOutput, old + opOutput, reduction / static_cast(n) ) // ============================================================================= // INDEX REDUCE OPERATIONS // ============================================================================= DECLARE_INDEX_REDUCE_OP(IndexMax, -sd::DataTypeUtils::infOrMax(), opOutput.value > old.value, f1.value > f2.value) DECLARE_INDEX_REDUCE_OP(IndexMin, sd::DataTypeUtils::infOrMax(), opOutput.value < old.value, f1.value < f2.value) DECLARE_BINARY_COPY_OP(LogPoissonLoss, sd::math::sd_exp(d2) - static_cast(d1) * static_cast(d2), sd::math::sd_exp(d2) - static_cast(d1) * static_cast(d2), static_cast(d1), sd::math::sd_exp(params[0]) - static_cast(d1) * static_cast(params[0]) ) // LogicalNot - using existing binary copy pattern DECLARE_BINARY_COPY_OP(LogicalNot, static_cast(!((int)d1 && (int)d2)), static_cast(!(static_cast(d1) && static_cast(d2))), static_cast(d1), static_cast(119) ) // LogicalXor - bitwise XOR logic DECLARE_BINARY_COPY_OP(LogicalXor, static_cast((static_cast(d1) | static_cast(d2)) & ~(static_cast(d1) & static_cast(d2))), static_cast((static_cast(d1) | static_cast(d2)) & ~(static_cast(d1) & static_cast(d2))), static_cast(d1), static_cast(119) ) // LogicalAnd - bitwise AND logic DECLARE_BINARY_COPY_OP(LogicalAnd, static_cast(static_cast(d1) & static_cast(d2)), static_cast(static_cast(d1) & static_cast(d2)), static_cast(d1), static_cast(119) ) // LogicalOr - bitwise OR logic DECLARE_BINARY_COPY_OP(LogicalOr, static_cast(static_cast(d1) | static_cast(d2)), static_cast(static_cast(d1) | static_cast(d2)), static_cast(d1), static_cast(119) ) template class MatchCondition { private: static SD_HOST_DEVICE SD_INLINE Z op_logic(X d1, X compare, X eps, int mode) { switch (mode) { case 0: return static_cast(sd::math::sd_abs(d1 - compare) <= eps ? 1 : 0); case 1: return static_cast(sd::math::sd_abs(d1 - compare) > eps ? 1 : 0); case 2: return static_cast(d1 < compare ? 1 : 0); case 3: return static_cast(d1 > compare ? 1 : 0); case 4: return static_cast(d1 <= compare ? 1 : 0); case 5: return static_cast(d1 >= compare ? 1 : 0); case 6: return static_cast(sd::math::sd_abs(d1) < compare ? 1 : 0); case 7: return static_cast(sd::math::sd_abs(d1) > compare ? 1 : 0); case 8: return static_cast(sd::math::sd_isinf(d1) ? 1 : 0); case 9: return static_cast(sd::math::sd_isnan(d1) ? 1 : 0); case 10: return static_cast((d1 == compare) ? 1 : 0); case 11: return static_cast((d1 != compare) ? 1 : 0); case 12: return static_cast(sd::math::sd_abs(d1) >= compare ? 1 : 0); case 13: return static_cast(sd::math::sd_abs(d1) <= compare ? 1 : 0); case 14: return static_cast(!(sd::math::sd_isinf(d1) || sd::math::sd_isnan(d1)) ? 1 : 0); case 15: return static_cast(sd::math::sd_isinf(d1) || sd::math::sd_isnan(d1) ? 1 : 0); default: sd_printf("Undefined match condition: [%i]\n", mode); } return static_cast(d1); } public: static const bool requiresSpecialAccumulation = false; using InterType = typename AggregateType::type; // execSpecial signatures - matches what reduce_long.hpp expects static void execSpecial(const X *x, const sd::LongType *xShapeInfo, sd::LongType *extraParams, Z *result, const sd::LongType *resultShapeInfoBuffer, sd::LongType *dimension, sd::LongType dimensionLength, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffset) {} #ifdef __CUDACC__ static SD_INLINE SD_DEVICE void execSpecialCuda( const X *dx, const sd::LongType *xShapeInfo, sd::LongType *extraParams, Z *result, const sd::LongType *resultShapeInfo, sd::LongType *dimension, sd::LongType dimensionLength, Z *reductionBuffer, const sd::LongType *tadOnlyShapeInfo, const sd::LongType *tadOffsets) {} #endif // Core reduction operation methods - these use X* parameters for reduce_long SD_HOST_DEVICE SD_INLINE static Z startingValue(const X* input) { return static_cast(0); } SD_HOST_DEVICE SD_INLINE static InterType merge(InterType old, InterType opOutput, X* extraParams) { return old + opOutput; } SD_HOST_DEVICE SD_INLINE static InterType update(InterType old, InterType opOutput, X* extraParams) { return old + opOutput; } SD_HOST_DEVICE SD_INLINE static Z postProcess(InterType reduction, sd::LongType n, X* extraParams) { return static_cast(reduction); } // Core op methods - these use X* parameters for reduce_long to preserve comparison value types static SD_HOST_DEVICE SD_INLINE InterType op(X d1, X* extraParams) { if (extraParams == nullptr) return static_cast(0); X compare = extraParams[0]; X eps = extraParams[1]; auto mode = static_cast(extraParams[2]); return static_cast(op_logic(d1, compare, eps, mode)); } static SD_HOST_DEVICE SD_INLINE InterType op(X d1, X d2, X* extraParams) { if (extraParams == nullptr) { // If no extraParams, use d2 as compare value, default eps=0, mode=0 (equals) return static_cast(op_logic(d1, d2, static_cast(0), 0)); } // Use d2 as comparison value, extraParams for eps and mode X compare = d2; X eps = extraParams[0]; auto mode = static_cast(extraParams[1]); return static_cast(op_logic(d1, compare, eps, mode)); } static SD_HOST_DEVICE SD_INLINE InterType op(X d1, X d2) { // Default: compare d1 to d2 with eps=0 and mode=0 (equals) return static_cast(op_logic(d1, d2, static_cast(0), 0)); } // *** TEMPLATE OVERLOADS FOR DIFFERENT PARAMETER TYPES *** // Template overloads for X* parameters - only when X != Z template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v, InterType>::type op(X d1, ParamType* extraParams) { if (extraParams == nullptr) return static_cast(0); X compare = extraParams[0]; X eps = extraParams[1]; auto mode = static_cast(extraParams[2]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v, InterType>::type op(X d1, X d2, ParamType* extraParams) { if (extraParams == nullptr) { return static_cast(op_logic(d1, d2, static_cast(0), 0)); } X compare = d2; X eps = extraParams[0]; auto mode = static_cast(extraParams[1]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v, InterType>::type merge(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v, InterType>::type update(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v, Z>::type postProcess(InterType reduction, sd::LongType n, ParamType* extraParams) { return static_cast(reduction); } // Template overloads for sd::LongType* parameters template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type op(X d1, ParamType* extraParams) { if (extraParams == nullptr) return static_cast(0); X compare = static_cast(extraParams[0]); X eps = static_cast(extraParams[1]); auto mode = static_cast(extraParams[2]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type op(X d1, X d2, ParamType* extraParams) { if (extraParams == nullptr) { return static_cast(op_logic(d1, d2, static_cast(0), 0)); } X compare = d2; X eps = static_cast(extraParams[0]); auto mode = static_cast(extraParams[1]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type merge(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type update(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, Z>::type postProcess(InterType reduction, sd::LongType n, ParamType* extraParams) { return static_cast(reduction); } // Template overloads for float* parameters (for cases like bfloat16/bfloat16 with float* extraParams) template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type op(X d1, ParamType* extraParams) { if (extraParams == nullptr) return static_cast(0); X compare = static_cast(extraParams[0]); X eps = static_cast(extraParams[1]); auto mode = static_cast(extraParams[2]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type op(X d1, X d2, ParamType* extraParams) { if (extraParams == nullptr) { return static_cast(op_logic(d1, d2, static_cast(0), 0)); } X compare = d2; X eps = static_cast(extraParams[0]); auto mode = static_cast(extraParams[1]); return static_cast(op_logic(d1, compare, eps, mode)); } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type merge(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, InterType>::type update(InterType old, InterType opOutput, ParamType* extraParams) { return old + opOutput; } template static SD_HOST_DEVICE SD_INLINE typename std::enable_if && !std::is_same_v && !std::is_same_v, Z>::type postProcess(InterType reduction, sd::LongType n, ParamType* extraParams) { return static_cast(reduction); } }; // --- Specialization: std::basic_string (UTF-32) -> std::basic_string (UTF-16) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static SD_HOST_DEVICE void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, // Z is std::basic_string const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE SD_INLINE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char16_t temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES / sizeof(char16_t) + 1]; const char32_t* input_data = d1.data(); const uint32_t input_length_char32_units = static_cast(d1.length()); sd::LongType required_bytes_for_utf16 = sd::unicode::offsetUtf32StringInUtf16(input_data, input_length_char32_units); if (required_bytes_for_utf16 > 0 && static_cast(required_bytes_for_utf16) < sizeof(temp_output_buffer)) { void* end_ptr = sd::unicode::utf32to16Ptr(input_data, input_data + input_length_char32_units, temp_output_buffer); size_t char16_units_written = static_cast(end_ptr) - temp_output_buffer; if (char16_units_written * sizeof(char16_t) == static_cast(required_bytes_for_utf16)) { return std::basic_string(temp_output_buffer, char16_units_written); } } return std::basic_string(); } }; // --- Specialization: std::basic_string (UTF-16) -> std::basic_string (UTF-32) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static SD_HOST_DEVICE SD_INLINE void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, // Z is std::basic_string const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE SD_INLINE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { char32_t temp_output_buffer[SD_STRING_ASSIGN_TEMP_BUFFER_BYTES / sizeof(char32_t) + 1]; const char16_t* input_data = d1.data(); const uint32_t input_length_char16_units = static_cast(d1.length()); sd::LongType required_bytes_for_utf32_data = sd::unicode::offsetUtf16StringInUtf32(input_data, input_length_char16_units); if (required_bytes_for_utf32_data > 0 && static_cast(required_bytes_for_utf32_data) < sizeof(temp_output_buffer)) { void* end_ptr = sd::unicode::utf16to32Ptr(input_data, input_data + input_length_char16_units, temp_output_buffer); size_t char32_units_written = static_cast(end_ptr) - temp_output_buffer; if (char32_units_written * sizeof(char32_t) == static_cast(required_bytes_for_utf32_data)) { return std::basic_string(temp_output_buffer, char32_units_written); } } return std::basic_string(); } }; // --- Identity Specializations (Redundant in modernized version but included for completeness) --- template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static SD_HOST_DEVICE SD_INLINE void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE SD_INLINE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { return d1; } }; template <> class Assign, std::basic_string> { public: static const bool requiresSpecial = false; static SD_HOST_DEVICE SD_INLINE void execSpecial(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #ifdef __CUDACC__ static SD_DEVICE void execSpecialCuda(const std::basic_string *dx, const sd::LongType *xShapeBuffer, std::basic_string *result, const sd::LongType *resultShapeBuffer, std::basic_string *extraParams, sd::LongType *allocationPointer, std::basic_string *reductionPointer, const sd::LongType *tadShapeInfo, const sd::LongType *tadOffsets) {} #endif SD_HOST_DEVICE SD_INLINE static std::basic_string op(const std::basic_string& d1, std::basic_string * /*params*/) { return d1; } }; } // namespace simdOps #endif