1use std::collections::HashMap;
18use std::f64::consts::{LN_10, LN_2};
19use std::sync::Arc;
20
21use sqlparser::ast::{
22 BinaryOperator, DataType, Expr, Function, FunctionArg, FunctionArgExpr, FunctionArguments,
23 ObjectNamePart, UnaryOperator,
24};
25
26use crate::colref::{ColRef, IdentCasing, Match};
27use crate::constructors::{
28 add, as_const, div, finite_num, func, func1, is_zero, mul, neg, num, one, sign, square, sub,
29 zero,
30};
31use crate::error::{DiffError, Result};
32
33const SUPPORTED: &str = "ddx differentiates the operators + - * /, unary calls to \
36sin/cos/tan/asin/acos/atan/exp/ln/log2/log10/sqrt/sinh/cosh/tanh/abs, power(...) with a \
37constant base or exponent, casts to a numeric type, and column/literal leaves";
38
39pub type Rule = Arc<dyn Fn(&Expr) -> Result<Expr> + Send + Sync>;
44
45#[derive(Clone)]
50pub struct RuleRegistry {
51 unary: HashMap<String, Rule>,
52}
53
54impl Default for RuleRegistry {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60fn rule(f: impl Fn(&Expr) -> Expr + Send + Sync + 'static) -> Rule {
62 Arc::new(move |u| Ok(f(u)))
63}
64
65impl RuleRegistry {
66 pub fn new() -> Self {
70 let mut unary: HashMap<String, Rule> = HashMap::new();
71
72 unary.insert("sin".into(), rule(|u| func1("cos", u.clone())));
74 unary.insert("cos".into(), rule(|u| neg(func1("sin", u.clone()))));
75 unary.insert(
76 "tan".into(),
77 rule(|u| div(one(), square(func1("cos", u.clone())))),
78 );
79 unary.insert(
81 "asin".into(),
82 rule(|u| div(one(), func1("sqrt", sub(one(), square(u.clone()))))),
83 );
84 unary.insert(
85 "acos".into(),
86 rule(|u| neg(div(one(), func1("sqrt", sub(one(), square(u.clone())))))),
87 );
88 unary.insert(
89 "atan".into(),
90 rule(|u| div(one(), add(one(), square(u.clone())))),
91 );
92 unary.insert("exp".into(), rule(|u| func1("exp", u.clone())));
94 unary.insert("ln".into(), rule(|u| div(one(), u.clone())));
95 unary.insert(
96 "log2".into(),
97 rule(|u| div(one(), mul(u.clone(), num(LN_2)))),
98 );
99 unary.insert(
100 "log10".into(),
101 rule(|u| div(one(), mul(u.clone(), num(LN_10)))),
102 );
103 unary.insert(
104 "sqrt".into(),
105 rule(|u| div(one(), mul(num(2.0), func1("sqrt", u.clone())))),
106 );
107 unary.insert("sinh".into(), rule(|u| func1("cosh", u.clone())));
109 unary.insert("cosh".into(), rule(|u| func1("sinh", u.clone())));
110 unary.insert(
111 "tanh".into(),
112 rule(|u| sub(one(), square(func1("tanh", u.clone())))),
113 );
114 unary.insert("abs".into(), rule(|u| sign(u.clone())));
121
122 RuleRegistry { unary }
123 }
124
125 pub fn register(&mut self, name: &str, rule: Rule) {
128 self.unary.insert(name.to_ascii_lowercase(), rule);
129 }
130
131 fn lookup(&self, name: &str) -> Option<&Rule> {
132 self.unary.get(name)
133 }
134}
135
136type Leaf<'a> = dyn Fn(&ColRef) -> Result<Expr> + 'a;
140
141pub fn differentiate(
145 expr: &Expr,
146 wrt: &ColRef,
147 casing: IdentCasing,
148 reg: &RuleRegistry,
149) -> Result<Expr> {
150 let leaf = |c: &ColRef| match c.classify(wrt, casing) {
151 Match::Is => Ok(one()),
152 Match::Not => Ok(zero()),
153 Match::Ambiguous => Err(DiffError::AmbiguousColumn(format!(
154 "occurrence of `{}` cannot be matched against differentiation \
155 variable `{}` — fully qualify it",
156 c.display(),
157 wrt.display()
158 ))),
159 };
160 linearize(expr, &leaf, reg)
161}
162
163pub fn jvp(
169 expr: &Expr,
170 seeds: &[(ColRef, Expr)],
171 casing: IdentCasing,
172 reg: &RuleRegistry,
173) -> Result<Expr> {
174 let leaf = |c: &ColRef| {
175 for (col, tangent) in seeds {
176 match c.classify(col, casing) {
177 Match::Is => return Ok(tangent.clone()),
178 Match::Ambiguous => {
179 return Err(DiffError::AmbiguousColumn(format!(
180 "occurrence of `{}` cannot be matched against seeded \
181 column `{}` — fully qualify it",
182 c.display(),
183 col.display()
184 )))
185 }
186 Match::Not => continue,
187 }
188 }
189 Ok(zero())
190 };
191 linearize(expr, &leaf, reg)
192}
193
194fn linearize(expr: &Expr, leaf: &Leaf, reg: &RuleRegistry) -> Result<Expr> {
196 match expr {
197 Expr::Identifier(_) | Expr::CompoundIdentifier(_) => {
199 let cr = ColRef::from_expr(expr)
200 .ok_or_else(|| DiffError::Internal("column expr yielded no ColRef".into()))?;
201 leaf(&cr)
202 }
203
204 Expr::Value(_) => Ok(zero()),
206
207 Expr::Nested(inner) => linearize(inner, leaf, reg),
210
211 Expr::Cast {
217 kind,
218 expr: inner,
219 data_type,
220 array,
221 format,
222 } => {
223 if !is_numeric_type(data_type) {
224 return Err(DiffError::NotImplemented(format!(
225 "differentiation through a cast to non-numeric type `{data_type}` \
226 is not supported"
227 )));
228 }
229 let du = linearize(inner, leaf, reg)?;
230 Ok(Expr::Cast {
231 kind: kind.clone(),
232 expr: Box::new(du),
233 data_type: data_type.clone(),
234 array: *array,
235 format: format.clone(),
236 })
237 }
238
239 Expr::UnaryOp {
241 op: UnaryOperator::Minus,
242 expr: inner,
243 } => Ok(neg(linearize(inner, leaf, reg)?)),
244 Expr::UnaryOp {
245 op: UnaryOperator::Plus,
246 expr: inner,
247 } => linearize(inner, leaf, reg),
248
249 Expr::BinaryOp { left, op, right } => linearize_binary(left, op, right, leaf, reg),
250
251 Expr::Function(f) => linearize_function(f, leaf, reg),
252
253 other => Err(DiffError::NotImplemented(format!(
254 "this expression cannot be differentiated: `{other}`. {SUPPORTED}; CASE, \
255 comparisons, subqueries, window functions, and string/temporal expressions are \
256 not differentiable"
257 ))),
258 }
259}
260
261fn linearize_binary(
263 left: &Expr,
264 op: &BinaryOperator,
265 right: &Expr,
266 leaf: &Leaf,
267 reg: &RuleRegistry,
268) -> Result<Expr> {
269 let da = linearize(left, leaf, reg)?;
270 let db = linearize(right, leaf, reg)?;
271 match op {
272 BinaryOperator::Plus => Ok(add(da, db)),
274 BinaryOperator::Minus => Ok(sub(da, db)),
276 BinaryOperator::Multiply => Ok(add(mul(da, right.clone()), mul(left.clone(), db))),
278 BinaryOperator::Divide => {
280 let numerator = sub(mul(da, right.clone()), mul(left.clone(), db));
281 Ok(div(numerator, square(right.clone())))
282 }
283 other => Err(DiffError::NotImplemented(format!(
284 "the operator `{other}` is not differentiable. {SUPPORTED}"
285 ))),
286 }
287}
288
289fn linearize_function(f: &Function, leaf: &Leaf, reg: &RuleRegistry) -> Result<Expr> {
291 let name = simple_func_name(f).ok_or_else(|| {
292 DiffError::NotImplemented(format!(
293 "cannot differentiate the call `{f}`: only an unqualified function name has a \
294 differentiation rule (a schema-qualified or otherwise complex name is left alone)"
295 ))
296 })?;
297 let args = positional_args(f).ok_or_else(|| {
298 DiffError::NotImplemented(format!(
299 "function `{name}` has non-positional arguments, which are not differentiable"
300 ))
301 })?;
302
303 if name == "power" || name == "pow" {
305 return linearize_power(&name, &args, leaf, reg);
306 }
307
308 if args.len() != 1 {
309 return Err(DiffError::NotImplemented(format!(
310 "no differentiation rule for `{name}` with {} arguments: the built-in function \
311 rules are unary, and `power` is the only two-argument rule",
312 args.len()
313 )));
314 }
315 let u = args[0];
316 let du = linearize(u, leaf, reg)?;
317 if is_zero(&du) {
319 return Ok(zero());
320 }
321 let outer = reg.lookup(&name).ok_or_else(|| {
322 DiffError::NotImplemented(format!(
323 "no differentiation rule for function `{name}`. {SUPPORTED}. Register a custom \
324 rule with `Ddx::register(\"{name}\", ...)`"
325 ))
326 })?(u)?;
327 Ok(mul(outer, du))
328}
329
330fn linearize_power(name: &str, args: &[&Expr], leaf: &Leaf, reg: &RuleRegistry) -> Result<Expr> {
336 if args.len() != 2 {
337 return Err(DiffError::NotImplemented(format!(
338 "{name}() expects exactly two arguments"
339 )));
340 }
341 let base = args[0];
342 let exponent = args[1];
343 match (as_const(base), as_const(exponent)) {
344 (_, Some(c)) => {
346 let dbase = linearize(base, leaf, reg)?;
347 if is_zero(&dbase) {
348 return Ok(zero());
349 }
350 let outer = mul(
355 finite_num(c)?,
356 func("power", vec![base.clone(), finite_num(c - 1.0)?]),
357 );
358 Ok(mul(outer, dbase))
359 }
360 (Some(a), None) => {
362 let dexp = linearize(exponent, leaf, reg)?;
363 if is_zero(&dexp) {
364 return Ok(zero());
365 }
366 let outer = mul(
370 func("power", vec![base.clone(), exponent.clone()]),
371 finite_num(a.ln())?,
372 );
373 Ok(mul(outer, dexp))
374 }
375 (None, None) => Err(DiffError::NotImplemented(
377 "cannot differentiate `power(base, exponent)` when both the base and the exponent \
378 depend on the differentiation variable; ddx handles it only when one side is a \
379 constant (e.g. `power(x, 2)` or `power(2, x)`). For the general u^v case, rewrite \
380 it as `exp(exponent * ln(base))` when the base is positive"
381 .into(),
382 )),
383 }
384}
385
386fn simple_func_name(f: &Function) -> Option<String> {
392 match f.name.0.as_slice() {
393 [ObjectNamePart::Identifier(id)] => Some(id.value.to_ascii_lowercase()),
394 _ => None,
395 }
396}
397
398pub(crate) fn positional_args(f: &Function) -> Option<Vec<&Expr>> {
402 match &f.args {
403 FunctionArguments::List(list) => {
404 let mut out = Vec::with_capacity(list.args.len());
405 for arg in &list.args {
406 match arg {
407 FunctionArg::Unnamed(FunctionArgExpr::Expr(e)) => out.push(e),
408 _ => return None,
409 }
410 }
411 Some(out)
412 }
413 _ => None,
414 }
415}
416
417pub(crate) fn is_numeric_type(dt: &DataType) -> bool {
422 matches!(
423 dt,
424 DataType::Numeric(_)
426 | DataType::Decimal(_)
427 | DataType::BigNumeric(_)
428 | DataType::BigDecimal(_)
429 | DataType::Dec(_)
430 | DataType::Float(_)
431 | DataType::FloatUnsigned(_)
432 | DataType::Float4
433 | DataType::Float32
434 | DataType::Float64
435 | DataType::Real
436 | DataType::RealUnsigned
437 | DataType::Float8
438 | DataType::Double(_)
439 | DataType::DoubleUnsigned(_)
440 | DataType::DoublePrecision
441 | DataType::DoublePrecisionUnsigned
442 | DataType::TinyInt(_)
444 | DataType::TinyIntUnsigned(_)
445 | DataType::UTinyInt
446 | DataType::Int2(_)
447 | DataType::Int2Unsigned(_)
448 | DataType::SmallInt(_)
449 | DataType::SmallIntUnsigned(_)
450 | DataType::USmallInt
451 | DataType::MediumInt(_)
452 | DataType::MediumIntUnsigned(_)
453 | DataType::Int(_)
454 | DataType::Int4(_)
455 | DataType::Int8(_)
456 | DataType::Int16
457 | DataType::Int32
458 | DataType::Int64
459 | DataType::Int128
460 | DataType::Int256
461 | DataType::Integer(_)
462 | DataType::IntUnsigned(_)
463 | DataType::Int4Unsigned(_)
464 | DataType::IntegerUnsigned(_)
465 | DataType::HugeInt
466 | DataType::UHugeInt
467 | DataType::UInt8
468 | DataType::UInt16
469 | DataType::UInt32
470 | DataType::UInt64
471 | DataType::UInt128
472 | DataType::UInt256
473 | DataType::BigInt(_)
474 | DataType::BigIntUnsigned(_)
475 | DataType::UBigInt
476 | DataType::Int8Unsigned(_)
477 | DataType::Signed
478 | DataType::SignedInteger
479 | DataType::Unsigned
480 | DataType::UnsignedInteger
481 )
482}