datafusion_functions/math/
power.rs1use std::any::Any;
20use std::sync::Arc;
21
22use super::log::LogFunc;
23
24use arrow::array::{ArrayRef, AsArray, Int64Array};
25use arrow::datatypes::{ArrowNativeTypeOp, DataType, Float64Type};
26use datafusion_common::{
27 arrow_datafusion_err, exec_datafusion_err, exec_err, internal_datafusion_err,
28 plan_datafusion_err, DataFusionError, Result, ScalarValue,
29};
30use datafusion_expr::expr::ScalarFunction;
31use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
32use datafusion_expr::{
33 ColumnarValue, Documentation, Expr, ScalarFunctionArgs, ScalarUDF, TypeSignature,
34};
35use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
36use datafusion_macros::user_doc;
37
38#[user_doc(
39 doc_section(label = "Math Functions"),
40 description = "Returns a base expression raised to the power of an exponent.",
41 syntax_example = "power(base, exponent)",
42 standard_argument(name = "base", prefix = "Numeric"),
43 standard_argument(name = "exponent", prefix = "Exponent numeric")
44)]
45#[derive(Debug)]
46pub struct PowerFunc {
47 signature: Signature,
48 aliases: Vec<String>,
49}
50
51impl Default for PowerFunc {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl PowerFunc {
58 pub fn new() -> Self {
59 use DataType::*;
60 Self {
61 signature: Signature::one_of(
62 vec![
63 TypeSignature::Exact(vec![Int64, Int64]),
64 TypeSignature::Exact(vec![Float64, Float64]),
65 ],
66 Volatility::Immutable,
67 ),
68 aliases: vec![String::from("pow")],
69 }
70 }
71}
72
73impl ScalarUDFImpl for PowerFunc {
74 fn as_any(&self) -> &dyn Any {
75 self
76 }
77 fn name(&self) -> &str {
78 "power"
79 }
80
81 fn signature(&self) -> &Signature {
82 &self.signature
83 }
84
85 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
86 match arg_types[0] {
87 DataType::Int64 => Ok(DataType::Int64),
88 _ => Ok(DataType::Float64),
89 }
90 }
91
92 fn aliases(&self) -> &[String] {
93 &self.aliases
94 }
95
96 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
97 let args = ColumnarValue::values_to_arrays(&args.args)?;
98
99 let arr: ArrayRef = match args[0].data_type() {
100 DataType::Float64 => {
101 let bases = args[0].as_primitive::<Float64Type>();
102 let exponents = args[1].as_primitive::<Float64Type>();
103 let result = arrow::compute::binary::<_, _, _, Float64Type>(
104 bases,
105 exponents,
106 f64::powf,
107 )?;
108 Arc::new(result) as _
109 }
110 DataType::Int64 => {
111 let bases = downcast_named_arg!(&args[0], "base", Int64Array);
112 let exponents = downcast_named_arg!(&args[1], "exponent", Int64Array);
113 bases
114 .iter()
115 .zip(exponents.iter())
116 .map(|(base, exp)| match (base, exp) {
117 (Some(base), Some(exp)) => Ok(Some(base.pow_checked(
118 exp.try_into().map_err(|_| {
119 exec_datafusion_err!(
120 "Can't use negative exponents: {exp} in integer computation, please use Float."
121 )
122 })?,
123 ).map_err(|e| arrow_datafusion_err!(e))?)),
124 _ => Ok(None),
125 })
126 .collect::<Result<Int64Array>>()
127 .map(Arc::new)? as _
128 }
129
130 other => {
131 return exec_err!(
132 "Unsupported data type {other:?} for function {}",
133 self.name()
134 )
135 }
136 };
137
138 Ok(ColumnarValue::Array(arr))
139 }
140
141 fn simplify(
146 &self,
147 mut args: Vec<Expr>,
148 info: &dyn SimplifyInfo,
149 ) -> Result<ExprSimplifyResult> {
150 let exponent = args.pop().ok_or_else(|| {
151 plan_datafusion_err!("Expected power to have 2 arguments, got 0")
152 })?;
153 let base = args.pop().ok_or_else(|| {
154 plan_datafusion_err!("Expected power to have 2 arguments, got 1")
155 })?;
156
157 let exponent_type = info.get_data_type(&exponent)?;
158 match exponent {
159 Expr::Literal(value) if value == ScalarValue::new_zero(&exponent_type)? => {
160 Ok(ExprSimplifyResult::Simplified(Expr::Literal(
161 ScalarValue::new_one(&info.get_data_type(&base)?)?,
162 )))
163 }
164 Expr::Literal(value) if value == ScalarValue::new_one(&exponent_type)? => {
165 Ok(ExprSimplifyResult::Simplified(base))
166 }
167 Expr::ScalarFunction(ScalarFunction { func, mut args })
168 if is_log(&func) && args.len() == 2 && base == args[0] =>
169 {
170 let b = args.pop().unwrap(); Ok(ExprSimplifyResult::Simplified(b))
172 }
173 _ => Ok(ExprSimplifyResult::Original(vec![base, exponent])),
174 }
175 }
176
177 fn documentation(&self) -> Option<&Documentation> {
178 self.doc()
179 }
180}
181
182fn is_log(func: &ScalarUDF) -> bool {
184 func.inner().as_any().downcast_ref::<LogFunc>().is_some()
185}
186
187#[cfg(test)]
188mod tests {
189 use arrow::array::Float64Array;
190 use datafusion_common::cast::{as_float64_array, as_int64_array};
191
192 use super::*;
193
194 #[test]
195 fn test_power_f64() {
196 let args = ScalarFunctionArgs {
197 args: vec![
198 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
199 2.0, 2.0, 3.0, 5.0,
200 ]))), ColumnarValue::Array(Arc::new(Float64Array::from(vec![
202 3.0, 2.0, 4.0, 4.0,
203 ]))), ],
205 number_rows: 4,
206 return_type: &DataType::Float64,
207 };
208 let result = PowerFunc::new()
209 .invoke_with_args(args)
210 .expect("failed to initialize function power");
211
212 match result {
213 ColumnarValue::Array(arr) => {
214 let floats = as_float64_array(&arr)
215 .expect("failed to convert result to a Float64Array");
216 assert_eq!(floats.len(), 4);
217 assert_eq!(floats.value(0), 8.0);
218 assert_eq!(floats.value(1), 4.0);
219 assert_eq!(floats.value(2), 81.0);
220 assert_eq!(floats.value(3), 625.0);
221 }
222 ColumnarValue::Scalar(_) => {
223 panic!("Expected an array value")
224 }
225 }
226 }
227
228 #[test]
229 fn test_power_i64() {
230 let args = ScalarFunctionArgs {
231 args: vec![
232 ColumnarValue::Array(Arc::new(Int64Array::from(vec![2, 2, 3, 5]))), ColumnarValue::Array(Arc::new(Int64Array::from(vec![3, 2, 4, 4]))), ],
235 number_rows: 4,
236 return_type: &DataType::Int64,
237 };
238 let result = PowerFunc::new()
239 .invoke_with_args(args)
240 .expect("failed to initialize function power");
241
242 match result {
243 ColumnarValue::Array(arr) => {
244 let ints = as_int64_array(&arr)
245 .expect("failed to convert result to a Int64Array");
246
247 assert_eq!(ints.len(), 4);
248 assert_eq!(ints.value(0), 8);
249 assert_eq!(ints.value(1), 4);
250 assert_eq!(ints.value(2), 81);
251 assert_eq!(ints.value(3), 625);
252 }
253 ColumnarValue::Scalar(_) => {
254 panic!("Expected an array value")
255 }
256 }
257 }
258}