diff --git a/velox/docs/functions/presto/math.rst b/velox/docs/functions/presto/math.rst index 82a5a73594b31..510cf24c47a55 100644 --- a/velox/docs/functions/presto/math.rst +++ b/velox/docs/functions/presto/math.rst @@ -319,6 +319,12 @@ Probability Functions: inverse_cdf probability (p): P(N < n). The a, b parameters must be positive real values (all of type DOUBLE). The probability p must lie on the interval [0, 1]. +.. function:: inverse_laplace_cdf(mean, scale, p) → double + + Compute the inverse of the Laplace cdf with given mean and scale parameters for the + cumulative probability (p): P(N < n). The mean must be a real value and the + scale must be a positive real value (both of type DOUBLE). + The probability p must lie on the interval [0, 1]. ==================================== Statistical Functions diff --git a/velox/functions/prestosql/Probability.h b/velox/functions/prestosql/Probability.h index d3517ee2afd48..416135c4968dd 100644 --- a/velox/functions/prestosql/Probability.h +++ b/velox/functions/prestosql/Probability.h @@ -249,5 +249,22 @@ struct WeibullCDFFunction { } }; +template +struct InverseLaplaceCDFFunction { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE void + call(double& result, double location, double scale, double p) { + VELOX_USER_CHECK_GT(scale, 0, "scale must be greater than 0"); + VELOX_USER_CHECK(p >= 0 && p <= 1, "p must be in the interval [0, 1]") + + if (std::isnan(location) || std::isinf(location)) { + result = std::numeric_limits::quiet_NaN(); + } else { + boost::math::laplace_distribution<> laplaceDist(location, scale); + result = boost::math::quantile(laplaceDist, p); + } + } +}; + } // namespace } // namespace facebook::velox::functions diff --git a/velox/functions/prestosql/registration/ArithmeticFunctionsRegistration.cpp b/velox/functions/prestosql/registration/ArithmeticFunctionsRegistration.cpp index 3984366fc9ae7..f77f2b0160c87 100644 --- a/velox/functions/prestosql/registration/ArithmeticFunctionsRegistration.cpp +++ b/velox/functions/prestosql/registration/ArithmeticFunctionsRegistration.cpp @@ -189,6 +189,8 @@ void registerSimpleFunctions(const std::string& prefix) { Map>({prefix + "cosine_similarity"}); registerFunction( {prefix + "weibull_cdf"}); + registerFunction( + {prefix + "inverse_laplace_cdf"}); } } // namespace diff --git a/velox/functions/prestosql/tests/ProbabilityTest.cpp b/velox/functions/prestosql/tests/ProbabilityTest.cpp index 8ccbd82d12610..a9da5c3a5932f 100644 --- a/velox/functions/prestosql/tests/ProbabilityTest.cpp +++ b/velox/functions/prestosql/tests/ProbabilityTest.cpp @@ -453,5 +453,33 @@ TEST_F(ProbabilityTest, weibullCDF) { weibullCDF(kDoubleMin, kNan, kDoubleMax), "b must be greater than 0"); } +TEST_F(ProbabilityTest, inverseLaplaceCDF) { + const auto inverseLaplaceCDF = [&](std::optional location, + std::optional scale, + std::optional p) { + return evaluateOnce( + "inverse_laplace_cdf(c0, c1, c2)", location, scale, p); + }; + + EXPECT_EQ(inverseLaplaceCDF(0.0, 1.0, 0.5), 0.0); + EXPECT_EQ(inverseLaplaceCDF(5.0, 2.0, 0.5), 5.0); + + VELOX_ASSERT_THROW( + inverseLaplaceCDF(1.0, 1.0, kNan), "p must be in the interval [0, 1]"); + VELOX_ASSERT_THROW( + inverseLaplaceCDF(1.0, 1.0, 2.0), "p must be in the interval [0, 1]"); + + EXPECT_EQ(inverseLaplaceCDF(10.0, kDoubleMax, 0.999999999999), kInf); + EXPECT_EQ(inverseLaplaceCDF(10.0, kDoubleMin, 0.000000000001), 10.0); + VELOX_ASSERT_THROW( + inverseLaplaceCDF(1.0, kNan, 0.5), "scale must be greater than 0"); + VELOX_ASSERT_THROW( + inverseLaplaceCDF(1.0, -1.0, 0.5), "scale must be greater than 0"); + + EXPECT_THAT(inverseLaplaceCDF(kInf, 1.0, 0.5), IsNan()); + EXPECT_THAT(inverseLaplaceCDF(kNan, 1.0, 0.5), IsNan()); + EXPECT_THAT(inverseLaplaceCDF(kDoubleMax, 1.0, 0.5), kDoubleMax); + EXPECT_THAT(inverseLaplaceCDF(kDoubleMin, 1.0, 0.5), kDoubleMin); +} } // namespace } // namespace facebook::velox