Skip to main content

simsym/calculus/
diff.rs

1#[path = "trig_diff.rs"]
2mod trig_diff;
3
4use crate::expr::{const_, pow, Expr, ExprKind};
5use crate::rational::Rational;
6use crate::symbol::Symbol;
7
8pub fn diff(expr: Expr, var: Symbol) -> Expr {
9    let raw = diff_without_simplify(expr, var);
10    #[cfg(feature = "simplify")]
11    {
12        raw.simplify()
13    }
14    #[cfg(not(feature = "simplify"))]
15    {
16        raw
17    }
18}
19
20/// Symbolic derivative without calling [`Expr::simplify`] (no-op when the `simplify` feature is off).
21///
22/// Prefer this when the simplified form is expensive to compute but a raw
23/// derivative tree is enough (e.g. numeric spot-checks via [`Expr::eval_f64`]).
24pub fn diff_without_simplify(expr: Expr, var: Symbol) -> Expr {
25    diff_expr(&expr, var)
26}
27
28/// Differentiate without simplify (internal building block).
29pub(crate) fn diff_expr(e: &Expr, var: Symbol) -> Expr {
30    diff_kind(e.kind(), var)
31}
32
33fn diff_kind(kind: &ExprKind, var: Symbol) -> Expr {
34    match kind {
35        ExprKind::Const(_) => const_(Rational::zero()),
36        ExprKind::Var(s) => {
37            if *s == var {
38                const_(Rational::one())
39            } else {
40                const_(Rational::zero())
41            }
42        }
43        ExprKind::Add(a, b) => diff_expr(a, var) + diff_expr(b, var),
44        ExprKind::Sub(a, b) => diff_expr(a, var) - diff_expr(b, var),
45        ExprKind::Neg(e) => -diff_expr(e, var),
46        ExprKind::Mul(f, g) => {
47            let fp = diff_expr(f, var);
48            let gp = diff_expr(g, var);
49            fp * g.clone() + f.clone() * gp
50        }
51        ExprKind::Div(f, g) => {
52            let fp = diff_expr(f, var);
53            let gp = diff_expr(g, var);
54            let g_c = g.clone();
55            (fp * g_c.clone() - f.clone() * gp) / pow(g_c, const_(Rational::from(2)))
56        }
57        ExprKind::Pow(base, exp) => diff_pow(base, exp, var),
58        ExprKind::Sin(e) => diff_expr(e, var) * crate::expr::cos(e.clone()),
59        ExprKind::Cos(e) => -diff_expr(e, var) * crate::expr::sin(e.clone()),
60        ExprKind::Tan(e) => {
61            diff_expr(e, var) / pow(crate::expr::cos(e.clone()), const_(Rational::from(2)))
62        }
63        ExprKind::Exp(e) => diff_expr(e, var) * crate::expr::exp(e.clone()),
64        ExprKind::Ln(e) => diff_expr(e, var) / e.clone(),
65        ExprKind::Atan(e) => {
66            diff_expr(e, var) / (const_(Rational::one()) + pow(e.clone(), const_(Rational::from(2))))
67        }
68        k => trig_diff::diff_trig_kind(k, var)
69            .expect("extended trigonometric kinds are handled in trig_diff"),
70    }
71}
72
73fn diff_pow(base: &Expr, exp: &Expr, var: Symbol) -> Expr {
74    if let ExprKind::Var(s) = base.kind() {
75        if s.name() == "e" {
76            if is_var_expr(exp, var) {
77                return diff_expr(&crate::expr::exp(exp.clone()), var);
78            }
79        }
80    }
81    if let ExprKind::Const(n) = exp.kind() {
82        if let Some(n) = n.try_as_rational() {
83            if let Some(k) = n.as_integer() {
84            if k == 0 {
85                return const_(Rational::zero());
86            }
87            if k == 1 {
88                return diff_expr(base, var);
89            }
90            return const_(Rational::from(k))
91                * pow(base.clone(), const_(Rational::from(k - 1)))
92                * diff_expr(base, var);
93            }
94        }
95    }
96    let up = diff_expr(base, var);
97    let vp = diff_expr(exp, var);
98    let term1 = exp.clone() * pow(base.clone(), exp.clone() - const_(Rational::one())) * up;
99    let u_ln = base.clone();
100    let term2 = pow(base.clone(), exp.clone()) * crate::expr::ln(u_ln) * vp;
101    term1 + term2
102}
103
104fn is_var_expr(e: &Expr, var: Symbol) -> bool {
105    matches!(e.kind(), ExprKind::Var(s) if *s == var)
106}