diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 84c4bbcd73..76c25d4cd0 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -759,6 +759,20 @@ class Math { } return p_target; } + + static _ALWAYS_INLINE_ double sigmoid_affine(double p_x, double p_amplitude, double p_y_translation) { + return p_amplitude / (1.0 + ::exp(-p_x)) + p_y_translation; + } + static _ALWAYS_INLINE_ float sigmoid_affine(float p_x, float p_amplitude, float p_y_translation) { + return p_amplitude / (1.0f + expf(-p_x)) + p_y_translation; + } + + static _ALWAYS_INLINE_ double sigmoid_affine_approx(double p_x, double p_amplitude, double p_y_translation) { + return p_amplitude * (0.5 + p_x / (4.0 + fabs(p_x))) + p_y_translation; + } + static _ALWAYS_INLINE_ float sigmoid_affine_approx(float p_x, float p_amplitude, float p_y_translation) { + return p_amplitude * (0.5f + p_x / (4.0f + fabsf(p_x))) + p_y_translation; + } }; #endif // MATH_FUNCS_H diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 8e4231c90e..03edd6cc52 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -641,6 +641,22 @@ double VariantUtilityFunctions::pingpong(double value, double length) { return Math::pingpong(value, length); } +double VariantUtilityFunctions::sigmoid(double x) { + return Math::sigmoid_affine(x, 1.0, 0.0); +} + +double VariantUtilityFunctions::sigmoid_approx(double x) { + return Math::sigmoid_affine_approx(x, 1.0, 0.0); +} + +double VariantUtilityFunctions::sigmoid_affine(double x, double amplitude, double y_translation) { + return Math::sigmoid_affine(x, amplitude, y_translation); +} + +double VariantUtilityFunctions::sigmoid_affine_approx(double x, double amplitude, double y_translation) { + return Math::sigmoid_affine_approx(x, amplitude, y_translation); +} + Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1789,6 +1805,11 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(nearest_po2, sarray("value"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(pingpong, sarray("value", "length"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(sigmoid, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(sigmoid_approx, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(sigmoid_affine, sarray("x", "amplitude", "y_translation"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(sigmoid_affine_approx, sarray("x", "amplitude", "y_translation"), Variant::UTILITY_FUNC_TYPE_MATH); + // Random FUNCBIND(randomize, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM); diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h index 067988e246..a3eb386263 100644 --- a/core/variant/variant_utility.h +++ b/core/variant/variant_utility.h @@ -117,6 +117,10 @@ struct VariantUtilityFunctions { static double clampf(double x, double min, double max); static int64_t clampi(int64_t x, int64_t min, int64_t max); static int64_t nearest_po2(int64_t x); + static double sigmoid(double x); + static double sigmoid_approx(double x); + static double sigmoid_affine(double x, double amplitude, double y_translation); + static double sigmoid_affine_approx(double x, double amplitude, double y_translation); // Random static void randomize(); static int64_t randi(); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 660783a84a..25316bb7bd 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1175,6 +1175,84 @@ [/codeblocks] + + + + + Computes the sigmoid for [param x], which maps the input value into the range (0, 1). + The sigmoid function is defined as: + [codeblock] + sigmoid(x) = 1 / (1 + exp(-x)) + [/codeblock] + This is the most accurate implementation of the sigmoid. + [codeblock] + var result = sigmoid(0.0) # result is 0.5 + var result = sigmoid(1.0) # result is approximately 0.7310 + var result = sigmoid(-1.0) # result is approximately 0.2689 + var result = sigmoid(5.0) # result is approximately 0.9933 + [/codeblock] + [b]Note:[/b] For faster but less accurate approximation, see [method sigmoid_approx]. + + + + + + + + + Computes an affine-transformed sigmoid for [param x], which allows scaling by [param amplitude] and translation by [param y_translation]. + The affine sigmoid function is defined as: + [codeblock] + sigmoid_affine(x, amplitude, y_translation) = (amplitude / (1 + exp(-x))) + y_translation + [/codeblock] + This function modifies the standard sigmoid by introducing scaling and vertical translation. + [codeblock] + var result = sigmoid_affine(0.0, 1.0, 0.0) # result is 0.5 + var result = sigmoid_affine(1.0, 2.0, -1.0) # result is approximately 0.4621 + var result = sigmoid_affine(-1.0, 3.0, 2.0) # result is approximately 2.8068 + var result = sigmoid_affine(1.0, 2.0, 2.5) # result is approximately 3.9621 + [/codeblock] + [b]Note:[/b] This is a more accurate but computationally heavier version of the affine sigmoid. For faster approximations, see [method sigmoid_affine_approx]. + + + + + + + + + Computes an approximation of the affine-transformed sigmoid function for [param x], allowing scaling by [param amplitude] and translation by [param y_translation]. + The approximation function is defined as: + [codeblock] + affine_sigmoid_approx(x, amplitude, y_translation) = amplitude * (0.5 + (x / (4 + abs(x)))) + y_translation + [/codeblock] + This function approximates the affine sigmoid, offering faster computation at the cost of some precision. It is useful in performance-sensitive environments where both transformation and speed are needed. + [codeblock] + var result = sigmoid_affine_approx(0.0, 1.0, 0.0) # result is 0.5 + var result = sigmoid_affine_approx(2.0, 2.0, 1.0) # result is approximately 2.6667 + var result = sigmoid_affine_approx(-1.0, 3.0, 0.5) # result is 1.4 + var result = sigmoid_affine_approx(1.0, 2.0, 2.5) # result is 3.9 + [/codeblock] + + + + + + + Computes an approximation of the sigmoid function for [param x], which maps the input value into the range (0, 1). + The approximation function is defined as: + [codeblock] + sigmoid_approx(x) = 0.5 + (x / (4 + abs(x))) + [/codeblock] + This function is faster than the standard [method sigmoid], especially useful in performance-sensitive environments where a balance between accuracy and speed is desired. + [codeblock] + var result = sigmoid_approx(0.0) # result is 0.5 + var result = sigmoid_approx(2.0) # result is approximately 0.8333 + var result = sigmoid_approx(-1.0) # result is 0.3 + var result = sigmoid_approx(5.0) # result is approximately 1.0555 + [/codeblock] + + diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h index 1a692e7eff..ef437b9c5c 100644 --- a/tests/core/math/test_math_funcs.h +++ b/tests/core/math/test_math_funcs.h @@ -641,6 +641,20 @@ TEST_CASE_TEMPLATE("[Math] bezier_interpolate", T, float, double) { CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)1.0) == doctest::Approx((T)1.0)); } +TEST_CASE_TEMPLATE("[Math] sigmoid_affine", T, float, double) { + CHECK(Math::sigmoid_affine((T)0.0, (T)1.0, (T)0.0) == doctest::Approx((T)0.5)); + CHECK(Math::sigmoid_affine((T)1.0, (T)2.0, (T)-1.0) == doctest::Approx((T)0.4621)); + CHECK(Math::sigmoid_affine((T)-1.0, (T)3.0, (T)2.0) == doctest::Approx((T)2.8068)); + CHECK(Math::sigmoid_affine((T)1.0, (T)2.0, (T)2.5) == doctest::Approx((T)3.9621)); +} + +TEST_CASE_TEMPLATE("[Math] sigmoid_affine_approx", T, float, double) { + CHECK(Math::sigmoid_affine_approx((T)0.0, (T)1.0, (T)0.0) == doctest::Approx((T)0.5)); + CHECK(Math::sigmoid_affine_approx((T)2.0, (T)2.0, (T)1.0) == doctest::Approx((T)2.6667)); + CHECK(Math::sigmoid_affine_approx((T)-1.0, (T)3.0, (T)0.5) == doctest::Approx((T)1.4)); + CHECK(Math::sigmoid_affine_approx((T)1.0, (T)2.0, (T)2.5) == doctest::Approx((T)3.9)); +} + } // namespace TestMath #endif // TEST_MATH_FUNCS_H