mathhook_core/algebra/simplification/
logarithmic.rs

1//! Logarithmic Function Simplification Strategies
2//!
3//! Implements algebraic rewrite rules for logarithmic functions (log, ln).
4
5use super::strategy::SimplificationStrategy;
6use crate::core::commutativity::Commutativity;
7use crate::core::{Expression, Number};
8
9/// Logarithm (base 10) simplification strategy
10pub struct LogarithmSimplificationStrategy;
11
12impl SimplificationStrategy for LogarithmSimplificationStrategy {
13    fn simplify(&self, args: &[Expression]) -> Expression {
14        if args.len() == 1 {
15            match &args[0] {
16                Expression::Number(Number::Integer(n)) if *n == 1 => Expression::integer(0),
17
18                Expression::Number(Number::Integer(n)) if *n == 10 => Expression::integer(1),
19
20                Expression::Pow(base, exp) => Expression::mul(vec![
21                    exp.as_ref().clone(),
22                    Expression::function("log", vec![base.as_ref().clone()]),
23                ]),
24
25                Expression::Mul(factors) => {
26                    let commutativity =
27                        Commutativity::combine(factors.iter().map(|f| f.commutativity()));
28
29                    if commutativity.can_sort() {
30                        let log_terms: Vec<Expression> = factors
31                            .iter()
32                            .map(|f| Expression::function("log", vec![f.clone()]))
33                            .collect();
34                        Expression::add(log_terms)
35                    } else {
36                        Expression::function("log", args.to_vec())
37                    }
38                }
39
40                _ => Expression::function("log", args.to_vec()),
41            }
42        } else if args.len() == 2 {
43            let x = &args[0];
44            let base = &args[1];
45
46            if x == base {
47                Expression::integer(1)
48            } else if x.is_one() {
49                Expression::integer(0)
50            } else {
51                Expression::function("log", args.to_vec())
52            }
53        } else {
54            Expression::function("log", args.to_vec())
55        }
56    }
57
58    fn applies_to(&self, args: &[Expression]) -> bool {
59        !args.is_empty() && args.len() <= 2
60    }
61
62    fn name(&self) -> &str {
63        "LogarithmSimplificationStrategy"
64    }
65}
66
67/// Natural logarithm (ln) simplification strategy
68pub struct NaturalLogSimplificationStrategy;
69
70impl SimplificationStrategy for NaturalLogSimplificationStrategy {
71    fn simplify(&self, args: &[Expression]) -> Expression {
72        if args.len() == 1 {
73            match &args[0] {
74                Expression::Number(Number::Integer(n)) if *n == 1 => Expression::integer(0),
75
76                Expression::Function {
77                    name,
78                    args: inner_args,
79                } if name.as_ref() == "exp" && inner_args.len() == 1 => inner_args[0].clone(),
80
81                Expression::Pow(base, exp) => Expression::mul(vec![
82                    exp.as_ref().clone(),
83                    Expression::function("ln", vec![base.as_ref().clone()]),
84                ]),
85
86                _ => Expression::function("ln", args.to_vec()),
87            }
88        } else {
89            Expression::function("ln", args.to_vec())
90        }
91    }
92
93    fn applies_to(&self, args: &[Expression]) -> bool {
94        args.len() == 1
95    }
96
97    fn name(&self) -> &str {
98        "NaturalLogSimplificationStrategy"
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use crate::{expr, symbol};
106
107    #[test]
108    fn test_log_of_one() {
109        let strategy = LogarithmSimplificationStrategy;
110        let result = strategy.simplify(&[expr!(1)]);
111        assert_eq!(result, expr!(0));
112    }
113
114    #[test]
115    fn test_log_of_ten() {
116        let strategy = LogarithmSimplificationStrategy;
117        let result = strategy.simplify(&[expr!(10)]);
118        assert_eq!(result, expr!(1));
119    }
120
121    #[test]
122    fn test_log_power_rule() {
123        let strategy = LogarithmSimplificationStrategy;
124        let result = strategy.simplify(&[expr!(x ^ 2)]);
125
126        if let Expression::Mul(terms) = result {
127            assert_eq!(terms.len(), 2);
128        } else {
129            panic!("Expected multiplication");
130        }
131    }
132
133    #[test]
134    fn test_log_with_base() {
135        let strategy = LogarithmSimplificationStrategy;
136        let x = symbol!(x);
137        let result = strategy.simplify(&[x.clone().into(), x.into()]);
138        assert_eq!(result, expr!(1));
139    }
140
141    #[test]
142    fn test_ln_of_one() {
143        let strategy = NaturalLogSimplificationStrategy;
144        let result = strategy.simplify(&[expr!(1)]);
145        assert_eq!(result, expr!(0));
146    }
147
148    #[test]
149    fn test_ln_of_exp() {
150        let strategy = NaturalLogSimplificationStrategy;
151        let x = symbol!(x);
152        let exp_x = Expression::function("exp", vec![x.clone().into()]);
153        let result = strategy.simplify(&[exp_x]);
154        assert_eq!(result, x.into());
155    }
156
157    #[test]
158    fn test_ln_power_rule() {
159        let strategy = NaturalLogSimplificationStrategy;
160        let result = strategy.simplify(&[expr!(x ^ 3)]);
161
162        if let Expression::Mul(terms) = result {
163            assert_eq!(terms.len(), 2);
164        } else {
165            panic!("Expected multiplication");
166        }
167    }
168}