Skip to main content

datafusion_functions/math/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! "math" DataFusion functions
19
20use crate::math::monotonicity::*;
21use datafusion_common::{Result, exec_err};
22use datafusion_expr::ScalarUDF;
23use std::sync::Arc;
24
25pub mod abs;
26pub mod bounds;
27pub mod ceil;
28mod common;
29pub mod cot;
30mod decimal;
31pub mod factorial;
32pub mod floor;
33pub mod gcd;
34pub mod iszero;
35pub mod lcm;
36pub mod log;
37pub mod monotonicity;
38pub mod nans;
39pub mod nanvl;
40pub mod pi;
41pub mod power;
42pub mod random;
43pub mod round;
44pub mod signum;
45pub mod trunc;
46
47fn validate_sqrt_input(value: f64) -> Result<()> {
48    if value < 0.0 {
49        exec_err!("cannot take square root of a negative number")
50    } else {
51        Ok(())
52    }
53}
54
55// Create UDFs
56make_udf_function!(abs::AbsFunc, abs);
57make_math_unary_udf!(
58    AcosFunc,
59    acos,
60    acos,
61    super::acos_order,
62    super::bounds::acos_bounds,
63    true,
64    super::get_acos_doc
65);
66make_math_unary_udf!(
67    AcoshFunc,
68    acosh,
69    acosh,
70    super::acosh_order,
71    super::bounds::acosh_bounds,
72    true,
73    super::get_acosh_doc
74);
75make_math_unary_udf!(
76    AsinFunc,
77    asin,
78    asin,
79    super::asin_order,
80    super::bounds::asin_bounds,
81    true,
82    super::get_asin_doc
83);
84make_math_unary_udf!(
85    AsinhFunc,
86    asinh,
87    asinh,
88    super::asinh_order,
89    super::bounds::unbounded_bounds,
90    true,
91    super::get_asinh_doc
92);
93make_math_unary_udf!(
94    AtanFunc,
95    atan,
96    atan,
97    super::atan_order,
98    super::bounds::atan_bounds,
99    true,
100    super::get_atan_doc
101);
102make_math_unary_udf!(
103    AtanhFunc,
104    atanh,
105    atanh,
106    super::atanh_order,
107    super::bounds::unbounded_bounds,
108    true,
109    super::get_atanh_doc
110);
111make_math_binary_udf!(
112    Atan2,
113    atan2,
114    atan2,
115    super::atan2_order,
116    true,
117    super::get_atan2_doc
118);
119make_math_unary_udf!(
120    CbrtFunc,
121    cbrt,
122    cbrt,
123    super::cbrt_order,
124    super::bounds::unbounded_bounds,
125    true,
126    super::get_cbrt_doc
127);
128make_udf_function!(ceil::CeilFunc, ceil);
129make_math_unary_udf!(
130    CosFunc,
131    cos,
132    cos,
133    super::cos_order,
134    super::bounds::cos_bounds,
135    true,
136    super::get_cos_doc
137);
138make_math_unary_udf!(
139    CoshFunc,
140    cosh,
141    cosh,
142    super::cosh_order,
143    super::bounds::cosh_bounds,
144    true,
145    super::get_cosh_doc
146);
147make_udf_function!(cot::CotFunc, cot);
148make_math_unary_udf!(
149    DegreesFunc,
150    degrees,
151    to_degrees,
152    super::degrees_order,
153    super::bounds::unbounded_bounds,
154    true,
155    super::get_degrees_doc
156);
157make_math_unary_udf!(
158    ExpFunc,
159    exp,
160    exp,
161    super::exp_order,
162    super::bounds::exp_bounds,
163    true,
164    super::get_exp_doc
165);
166make_udf_function!(factorial::FactorialFunc, factorial);
167make_udf_function!(floor::FloorFunc, floor);
168make_udf_function!(log::LogFunc, log);
169make_udf_function!(gcd::GcdFunc, gcd);
170make_udf_function!(nans::IsNanFunc, isnan);
171make_udf_function!(iszero::IsZeroFunc, iszero);
172make_udf_function!(lcm::LcmFunc, lcm);
173make_math_unary_udf!(
174    LnFunc,
175    ln,
176    ln,
177    super::ln_order,
178    super::bounds::unbounded_bounds,
179    true,
180    super::get_ln_doc
181);
182make_math_unary_udf!(
183    Log2Func,
184    log2,
185    log2,
186    super::log2_order,
187    super::bounds::unbounded_bounds,
188    true,
189    super::get_log2_doc
190);
191make_math_unary_udf!(
192    Log10Func,
193    log10,
194    log10,
195    super::log10_order,
196    super::bounds::unbounded_bounds,
197    true,
198    super::get_log10_doc
199);
200make_udf_function!(nanvl::NanvlFunc, nanvl);
201make_udf_function!(pi::PiFunc, pi);
202make_udf_function!(power::PowerFunc, power);
203make_math_unary_udf!(
204    RadiansFunc,
205    radians,
206    to_radians,
207    super::radians_order,
208    super::bounds::radians_bounds,
209    true,
210    super::get_radians_doc
211);
212make_udf_function!(random::RandomFunc, random);
213make_udf_function!(round::RoundFunc, round);
214make_udf_function!(signum::SignumFunc, signum);
215make_math_unary_udf!(
216    SinFunc,
217    sin,
218    sin,
219    super::sin_order,
220    super::bounds::sin_bounds,
221    true,
222    super::get_sin_doc
223);
224make_math_unary_udf!(
225    SinhFunc,
226    sinh,
227    sinh,
228    super::sinh_order,
229    super::bounds::unbounded_bounds,
230    true,
231    super::get_sinh_doc
232);
233make_math_unary_udf!(
234    SqrtFunc,
235    sqrt,
236    sqrt,
237    super::sqrt_order,
238    super::bounds::sqrt_bounds,
239    true,
240    super::get_sqrt_doc,
241    Some(super::validate_sqrt_input)
242);
243make_math_unary_udf!(
244    TanFunc,
245    tan,
246    tan,
247    super::tan_order,
248    super::bounds::unbounded_bounds,
249    true,
250    super::get_tan_doc
251);
252make_math_unary_udf!(
253    TanhFunc,
254    tanh,
255    tanh,
256    super::tanh_order,
257    super::bounds::tanh_bounds,
258    true,
259    super::get_tanh_doc
260);
261make_udf_function!(trunc::TruncFunc, trunc);
262
263#[cfg(test)]
264mod strict_tests {
265    use super::*;
266    use arrow::datatypes::Field;
267    use datafusion_common::ScalarValue;
268    use datafusion_expr::{
269        ColumnarValue, ReturnFieldArgs, ScalarFunctionArgs, ScalarUDF,
270    };
271    use std::sync::Arc;
272
273    #[test]
274    fn strict_math_functions_propagate_nulls() {
275        let cases = vec![
276            (abs(), vec![ScalarValue::from(1.0)]),
277            (acos(), vec![ScalarValue::from(0.5)]),
278            (acosh(), vec![ScalarValue::from(1.5)]),
279            (asin(), vec![ScalarValue::from(0.5)]),
280            (asinh(), vec![ScalarValue::from(0.5)]),
281            (atan(), vec![ScalarValue::from(0.5)]),
282            (
283                atan2(),
284                vec![ScalarValue::from(0.5), ScalarValue::from(1.0)],
285            ),
286            (atanh(), vec![ScalarValue::from(0.5)]),
287            (cbrt(), vec![ScalarValue::from(8.0)]),
288            (ceil(), vec![ScalarValue::from(1.5)]),
289            (cos(), vec![ScalarValue::from(0.5)]),
290            (cosh(), vec![ScalarValue::from(0.5)]),
291            (cot(), vec![ScalarValue::from(0.5)]),
292            (degrees(), vec![ScalarValue::from(0.5)]),
293            (exp(), vec![ScalarValue::from(0.5)]),
294            (factorial(), vec![ScalarValue::from(5_i64)]),
295            (floor(), vec![ScalarValue::from(1.5)]),
296            (
297                gcd(),
298                vec![ScalarValue::from(48_i64), ScalarValue::from(18_i64)],
299            ),
300            (isnan(), vec![ScalarValue::from(1.0)]),
301            (iszero(), vec![ScalarValue::from(1.0)]),
302            (
303                lcm(),
304                vec![ScalarValue::from(4_i64), ScalarValue::from(5_i64)],
305            ),
306            (ln(), vec![ScalarValue::from(2.0)]),
307            (log(), vec![ScalarValue::from(10.0)]),
308            (
309                log(),
310                vec![ScalarValue::from(10.0), ScalarValue::from(100.0)],
311            ),
312            (log2(), vec![ScalarValue::from(2.0)]),
313            (log10(), vec![ScalarValue::from(10.0)]),
314            (
315                power(),
316                vec![ScalarValue::from(2.0), ScalarValue::from(3.0)],
317            ),
318            (radians(), vec![ScalarValue::from(90.0)]),
319            (round(), vec![ScalarValue::from(1.5)]),
320            (
321                round(),
322                vec![ScalarValue::from(1.5), ScalarValue::from(1_i32)],
323            ),
324            (signum(), vec![ScalarValue::from(-1.0)]),
325            (sin(), vec![ScalarValue::from(0.5)]),
326            (sinh(), vec![ScalarValue::from(0.5)]),
327            (sqrt(), vec![ScalarValue::from(4.0)]),
328            (tan(), vec![ScalarValue::from(0.5)]),
329            (tanh(), vec![ScalarValue::from(0.5)]),
330            (trunc(), vec![ScalarValue::from(1.5)]),
331            (
332                trunc(),
333                vec![ScalarValue::from(1.5), ScalarValue::from(1_i64)],
334            ),
335        ];
336
337        for (func, valid_args) in cases {
338            assert!(func.is_strict(), "{} should be marked strict", func.name());
339
340            for null_mask in 0..(1 << valid_args.len()) {
341                let mut args = valid_args.clone();
342                for (arg_idx, arg) in args.iter_mut().enumerate() {
343                    if null_mask & (1 << arg_idx) != 0 {
344                        *arg = ScalarValue::try_new_null(&arg.data_type()).unwrap();
345                    }
346                }
347
348                let result =
349                    invoke_with_scalar_args(&func, args).unwrap_or_else(|error| {
350                        panic!(
351                            "{} failed for NULL mask {null_mask:b}: {error}",
352                            func.name()
353                        )
354                    });
355                let expected_null = null_mask != 0;
356                let result = result.into_array(1).unwrap();
357                assert_eq!(
358                    result.null_count() == result.len(),
359                    expected_null,
360                    "{} returned {result:?} for NULL mask {null_mask:0width$b}",
361                    func.name(),
362                    width = valid_args.len(),
363                );
364            }
365        }
366    }
367
368    fn invoke_with_scalar_args(
369        func: &ScalarUDF,
370        args: Vec<ScalarValue>,
371    ) -> Result<ColumnarValue> {
372        let arg_fields = args
373            .iter()
374            .enumerate()
375            .map(|(idx, arg)| {
376                Arc::new(Field::new(
377                    format!("arg_{idx}"),
378                    arg.data_type(),
379                    arg.is_null(),
380                ))
381            })
382            .collect::<Vec<_>>();
383        let scalar_arguments = args.iter().map(Some).collect::<Vec<_>>();
384        let return_field = func.return_field_from_args(ReturnFieldArgs {
385            arg_fields: &arg_fields,
386            scalar_arguments: &scalar_arguments,
387        })?;
388        func.invoke_with_args(ScalarFunctionArgs {
389            args: args.into_iter().map(ColumnarValue::Scalar).collect(),
390            arg_fields,
391            number_rows: 1,
392            return_field,
393            config_options: Arc::new(Default::default()),
394        })
395    }
396}
397
398pub mod expr_fn {
399    export_functions!(
400        (abs, "returns the absolute value of a given number", num),
401        (acos, "returns the arc cosine or inverse cosine of a number", num),
402        (acosh, "returns inverse hyperbolic cosine", num),
403        (asin, "returns the arc sine or inverse sine of a number", num),
404        (asinh, "returns inverse hyperbolic sine", num),
405        (atan, "returns inverse tangent", num),
406        (atan2, "returns inverse tangent of a division given in the argument", y x),
407        (atanh, "returns inverse hyperbolic tangent", num),
408        (cbrt, "cube root of a number", num),
409        (ceil, "nearest integer greater than or equal to argument", num),
410        (cos, "cosine", num),
411        (cosh, "hyperbolic cosine", num),
412        (cot, "cotangent of a number", num),
413        (degrees, "converts radians to degrees", num),
414        (exp, "exponential", num),
415        (factorial, "factorial", num),
416        (floor, "nearest integer less than or equal to argument", num),
417        (gcd, "greatest common divisor", x y),
418        (isnan, "returns true if a given number is +NaN or -NaN otherwise returns false", num),
419        (iszero, "returns true if a given number is +0.0 or -0.0 otherwise returns false", num),
420        (lcm, "least common multiple", x y),
421        (ln, "natural logarithm (base e) of a number", num),
422        (log, "logarithm of a number for a particular `base`", base num),
423        (log2, "base 2 logarithm of a number", num),
424        (log10, "base 10 logarithm of a number", num),
425        (nanvl, "returns x if x is not NaN otherwise returns y", x y),
426        (pi, "Returns an approximate value of π",),
427        (power, "`base` raised to the power of `exponent`", base exponent),
428        (radians, "converts degrees to radians", num),
429        (random, "Returns a random value in the range 0.0 <= x < 1.0",),
430        (signum, "sign of the argument (-1, 0, +1)", num),
431        (sin, "sine", num),
432        (sinh, "hyperbolic sine", num),
433        (sqrt, "square root of a number", num),
434        (tan, "returns the tangent of a number", num),
435        (tanh, "returns the hyperbolic tangent of a number", num),
436        (round, "round to nearest integer", args,),
437        (trunc, "truncate toward zero, with optional precision", args,)
438    );
439}
440
441/// Returns all DataFusion functions defined in this package
442pub fn functions() -> Vec<Arc<ScalarUDF>> {
443    vec![
444        abs(),
445        acos(),
446        acosh(),
447        asin(),
448        asinh(),
449        atan(),
450        atan2(),
451        atanh(),
452        cbrt(),
453        ceil(),
454        cos(),
455        cosh(),
456        cot(),
457        degrees(),
458        exp(),
459        factorial(),
460        floor(),
461        gcd(),
462        isnan(),
463        iszero(),
464        lcm(),
465        ln(),
466        log(),
467        log2(),
468        log10(),
469        nanvl(),
470        pi(),
471        power(),
472        radians(),
473        random(),
474        signum(),
475        sin(),
476        sinh(),
477        sqrt(),
478        tan(),
479        tanh(),
480        round(),
481        trunc(),
482    ]
483}
484
485#[cfg(test)]
486mod tests {
487    use arrow::datatypes::DataType;
488    use datafusion_common::ScalarValue;
489    use datafusion_expr::interval_arithmetic::Interval;
490
491    fn unbounded_interval(data_type: &DataType) -> Interval {
492        Interval::make_unbounded(data_type).unwrap()
493    }
494
495    fn one_to_inf_interval(data_type: &DataType) -> Interval {
496        Interval::try_new(
497            ScalarValue::new_one(data_type).unwrap(),
498            ScalarValue::try_from(data_type).unwrap(),
499        )
500        .unwrap()
501    }
502
503    fn zero_to_pi_interval(data_type: &DataType) -> Interval {
504        Interval::try_new(
505            ScalarValue::new_zero(data_type).unwrap(),
506            ScalarValue::new_pi_upper(data_type).unwrap(),
507        )
508        .unwrap()
509    }
510
511    fn assert_udf_evaluates_to_bounds(
512        udf: &datafusion_expr::ScalarUDF,
513        interval: Interval,
514        expected: Interval,
515    ) {
516        let input = vec![&interval];
517        let result = udf.evaluate_bounds(&input).unwrap();
518        assert_eq!(
519            result,
520            expected,
521            "Bounds check failed on UDF: {:?}",
522            udf.name()
523        );
524    }
525
526    #[test]
527    fn test_cases() -> crate::Result<()> {
528        let datatypes = [DataType::Float32, DataType::Float64];
529        let cases = datatypes
530            .iter()
531            .flat_map(|data_type| {
532                vec![
533                    (
534                        super::acos(),
535                        unbounded_interval(data_type),
536                        zero_to_pi_interval(data_type),
537                    ),
538                    (
539                        super::acosh(),
540                        unbounded_interval(data_type),
541                        Interval::make_non_negative_infinity_interval(data_type).unwrap(),
542                    ),
543                    (
544                        super::asin(),
545                        unbounded_interval(data_type),
546                        Interval::make_symmetric_half_pi_interval(data_type).unwrap(),
547                    ),
548                    (
549                        super::atan(),
550                        unbounded_interval(data_type),
551                        Interval::make_symmetric_half_pi_interval(data_type).unwrap(),
552                    ),
553                    (
554                        super::cos(),
555                        unbounded_interval(data_type),
556                        Interval::make_symmetric_unit_interval(data_type).unwrap(),
557                    ),
558                    (
559                        super::cosh(),
560                        unbounded_interval(data_type),
561                        one_to_inf_interval(data_type),
562                    ),
563                    (
564                        super::sin(),
565                        unbounded_interval(data_type),
566                        Interval::make_symmetric_unit_interval(data_type).unwrap(),
567                    ),
568                    (
569                        super::exp(),
570                        unbounded_interval(data_type),
571                        Interval::make_non_negative_infinity_interval(data_type).unwrap(),
572                    ),
573                    (
574                        super::sqrt(),
575                        unbounded_interval(data_type),
576                        Interval::make_non_negative_infinity_interval(data_type).unwrap(),
577                    ),
578                    (
579                        super::radians(),
580                        unbounded_interval(data_type),
581                        Interval::make_symmetric_pi_interval(data_type).unwrap(),
582                    ),
583                    (
584                        super::sqrt(),
585                        unbounded_interval(data_type),
586                        Interval::make_non_negative_infinity_interval(data_type).unwrap(),
587                    ),
588                ]
589            })
590            .collect::<Vec<_>>();
591
592        for (udf, interval, expected) in cases {
593            assert_udf_evaluates_to_bounds(&udf, interval, expected);
594        }
595
596        Ok(())
597    }
598}