ddx_core/lib.rs
1// SPDX-FileCopyrightText: 2026 Alexander Merose <al@merose.com> & ddx Authors
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! `ddx-core` — engine-neutral symbolic differentiation of SQL scalar
6//! expressions. The v1 core of [`ddx`](https://github.com/xqlsystems/ddx):
7//! write calculus directly in SQL and let the engine evaluate the derivative
8//! per row, the relational equivalent of `jax.vmap(jax.grad(f))`.
9//!
10//! ```sql
11//! SELECT i, grad(x * y, x) AS dfdx, grad(x * y, y) AS dfdy FROM g
12//! ```
13//!
14//! `grad`/`jvp` are **markers**, not row functions: they carry a
15//! differentiation request through parsing and are always rewritten away
16//! *before* execution. [`Ddx::rewrite_sql`] is the whole path — find every
17//! marker, differentiate what it wraps, splice the derivative back by source
18//! span, return plain SQL.
19//!
20//! The engine differentiates [`sqlparser::ast::Expr`] directly — there is no
21//! bespoke IR and no adapter layer; the AST *is* the IR (design.md §3.2). The
22//! single load-bearing dependency is [`sqlparser`], which is **re-exported**
23//! (see below) so downstream adapters cannot accidentally link a mismatched
24//! version.
25//!
26//! # What v1 supports
27//!
28//! `+ - * /`; the unary chain rule for the trig / inverse-trig / exp / log /
29//! hyperbolic set plus `abs`; `power` with a constant base or exponent;
30//! higher-order via nesting; through-aggregate via linearity
31//! (`AVG(grad(loss, theta))`). Anything else is a typed [`DiffError`], never a
32//! silently-wrong number (design principle 5).
33//!
34//! Scalar `vjp` is deliberately **not** part of the surface: the name is
35//! reserved for the query-level reverse-mode operation in
36//! [`ddx-ad`](https://docs.rs/ddx-ad) (design.md §3.6, §4, decision Q7).
37//!
38//! # `sqlparser` version policy
39//!
40//! `ddx-core`'s public API takes and returns `sqlparser::ast::Expr`, so a
41//! `sqlparser` bump is a breaking release of `ddx-core`. The version is pinned
42//! exactly (see `Cargo.toml`) and re-exported here as [`crate::sqlparser`]:
43//! always reach for `sqlparser` types through this re-export so your build
44//! links the same version the engine was compiled against (design.md §6, G2).
45
46#![forbid(unsafe_code)]
47
48/// The exact `sqlparser` this crate was built against, re-exported so downstream
49/// code links a matching version (design.md §6, G2).
50pub use sqlparser;
51
52mod colref;
53mod constructors;
54mod ddx;
55mod engine;
56mod error;
57mod rewrite;
58
59// The shared simulation harness — a random expression generator, a reference
60// interpreter, the numeric conditioning gates, and the failure reporter. Behind
61// a feature so it never ships in a normal build, but public when enabled so
62// every crate in the workspace fuzzes against the *same* generator: a
63// cross-crate agreement test proves nothing if each side invented its own idea
64// of "a random derivable expression".
65#[cfg(feature = "test-utils")]
66pub mod test_utils;
67
68/// Smart constructors for building derivative expressions — useful when writing
69/// a custom [`Rule`], which returns `f'(u)` as an [`sqlparser::ast::Expr`].
70pub mod build {
71 pub use crate::constructors::{
72 add, cast_double, div, finite_num, func, func1, mul, neg, one, sign, square, sub, zero,
73 };
74}
75
76pub use colref::{ColRef, IdentCasing, Match};
77pub use ddx::Ddx;
78pub use engine::{Rule, RuleRegistry};
79pub use error::{DiffError, Result};
80pub use rewrite::{ExplainStep, Explanation};