1use std::collections::{HashMap, HashSet, VecDeque};
12use std::fmt;
13
14#[derive(Debug, Clone, PartialEq)]
20pub enum SesError {
21 UnboundVariable(String),
23 MathDomainError(String),
25 ParseError(String),
27 DivisionByZero,
29}
30
31impl fmt::Display for SesError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 SesError::UnboundVariable(v) => write!(f, "unbound variable: {v}"),
35 SesError::MathDomainError(m) => write!(f, "math domain error: {m}"),
36 SesError::ParseError(m) => write!(f, "parse error: {m}"),
37 SesError::DivisionByZero => write!(f, "division by zero"),
38 }
39 }
40}
41
42impl std::error::Error for SesError {}
43
44#[derive(Debug, Clone, PartialEq)]
50pub enum SesExpr {
51 Num(f64),
53 Var(String),
55 Add(Box<SesExpr>, Box<SesExpr>),
57 Sub(Box<SesExpr>, Box<SesExpr>),
59 Mul(Box<SesExpr>, Box<SesExpr>),
61 Div(Box<SesExpr>, Box<SesExpr>),
63 Pow(Box<SesExpr>, Box<SesExpr>),
65 Neg(Box<SesExpr>),
67 Abs(Box<SesExpr>),
69 Sqrt(Box<SesExpr>),
71 Sin(Box<SesExpr>),
73 Cos(Box<SesExpr>),
75 Exp(Box<SesExpr>),
77 Ln(Box<SesExpr>),
79}
80
81impl SesExpr {
82 #[inline]
84 pub fn num(v: f64) -> Self {
85 SesExpr::Num(v)
86 }
87 #[inline]
89 pub fn var(name: impl Into<String>) -> Self {
90 SesExpr::Var(name.into())
91 }
92 #[inline]
94 #[allow(clippy::should_implement_trait)]
95 pub fn add(l: SesExpr, r: SesExpr) -> Self {
96 SesExpr::Add(Box::new(l), Box::new(r))
97 }
98 #[inline]
100 #[allow(clippy::should_implement_trait)]
101 pub fn sub(l: SesExpr, r: SesExpr) -> Self {
102 SesExpr::Sub(Box::new(l), Box::new(r))
103 }
104 #[inline]
106 #[allow(clippy::should_implement_trait)]
107 pub fn mul(l: SesExpr, r: SesExpr) -> Self {
108 SesExpr::Mul(Box::new(l), Box::new(r))
109 }
110 #[inline]
112 #[allow(clippy::should_implement_trait)]
113 pub fn div(l: SesExpr, r: SesExpr) -> Self {
114 SesExpr::Div(Box::new(l), Box::new(r))
115 }
116 #[inline]
118 pub fn pow(b: SesExpr, e: SesExpr) -> Self {
119 SesExpr::Pow(Box::new(b), Box::new(e))
120 }
121 #[inline]
123 #[allow(clippy::should_implement_trait)]
124 pub fn neg(e: SesExpr) -> Self {
125 SesExpr::Neg(Box::new(e))
126 }
127}
128
129impl fmt::Display for SesExpr {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "{}", infix_str(self, Precedence::Lowest))
136 }
137}
138
139#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
141enum Precedence {
142 Lowest,
143 AddSub,
144 MulDiv,
145 Unary,
146 Pow,
147}
148
149fn infix_str(e: &SesExpr, parent_prec: Precedence) -> String {
150 match e {
151 SesExpr::Num(v) => {
152 if v.fract() == 0.0 && v.abs() < 1e15 {
153 format!("{}", *v as i64)
154 } else {
155 format!("{v}")
156 }
157 }
158 SesExpr::Var(name) => name.clone(),
159 SesExpr::Add(l, r) => paren_if(
160 &format!(
161 "{} + {}",
162 infix_str(l, Precedence::AddSub),
163 infix_str(r, Precedence::AddSub)
164 ),
165 Precedence::AddSub,
166 parent_prec,
167 ),
168 SesExpr::Sub(l, r) => paren_if(
169 &format!(
170 "{} - {}",
171 infix_str(l, Precedence::AddSub),
172 infix_str(r, Precedence::MulDiv)
173 ),
174 Precedence::AddSub,
175 parent_prec,
176 ),
177 SesExpr::Mul(l, r) => paren_if(
178 &format!(
179 "{} * {}",
180 infix_str(l, Precedence::MulDiv),
181 infix_str(r, Precedence::MulDiv)
182 ),
183 Precedence::MulDiv,
184 parent_prec,
185 ),
186 SesExpr::Div(l, r) => paren_if(
187 &format!(
188 "{} / {}",
189 infix_str(l, Precedence::MulDiv),
190 infix_str(r, Precedence::Unary)
191 ),
192 Precedence::MulDiv,
193 parent_prec,
194 ),
195 SesExpr::Pow(b, exp) => paren_if(
196 &format!(
197 "{} ^ {}",
198 infix_str(b, Precedence::Unary),
199 infix_str(exp, Precedence::Pow)
200 ),
201 Precedence::Pow,
202 parent_prec,
203 ),
204 SesExpr::Neg(inner) => paren_if(
205 &format!("-{}", infix_str(inner, Precedence::Unary)),
206 Precedence::Unary,
207 parent_prec,
208 ),
209 SesExpr::Abs(inner) => format!("|{}|", infix_str(inner, Precedence::Lowest)),
210 SesExpr::Sqrt(inner) => format!("sqrt({})", infix_str(inner, Precedence::Lowest)),
211 SesExpr::Sin(inner) => format!("sin({})", infix_str(inner, Precedence::Lowest)),
212 SesExpr::Cos(inner) => format!("cos({})", infix_str(inner, Precedence::Lowest)),
213 SesExpr::Exp(inner) => format!("exp({})", infix_str(inner, Precedence::Lowest)),
214 SesExpr::Ln(inner) => format!("ln({})", infix_str(inner, Precedence::Lowest)),
215 }
216}
217
218fn paren_if(s: &str, my_prec: Precedence, parent_prec: Precedence) -> String {
219 if my_prec < parent_prec {
220 format!("({s})")
221 } else {
222 s.to_string()
223 }
224}
225
226#[derive(Debug, Clone)]
232pub struct SesSimplifierConfig {
233 pub max_passes: usize,
235 pub enable_constant_folding: bool,
237 pub enable_algebraic_rules: bool,
239 pub enable_trig_rules: bool,
241}
242
243impl Default for SesSimplifierConfig {
244 fn default() -> Self {
245 SesSimplifierConfig {
246 max_passes: 32,
247 enable_constant_folding: true,
248 enable_algebraic_rules: true,
249 enable_trig_rules: true,
250 }
251 }
252}
253
254pub struct SesRewriteRule {
260 pub name: String,
262 pub pattern: String,
264 pub applies: fn(&SesExpr) -> bool,
266 pub transform: Box<dyn Fn(SesExpr) -> SesExpr + Send + Sync>,
268}
269
270impl fmt::Debug for SesRewriteRule {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.debug_struct("SesRewriteRule")
273 .field("name", &self.name)
274 .field("pattern", &self.pattern)
275 .finish()
276 }
277}
278
279#[derive(Debug, Clone)]
285pub struct SesSimplificationStep {
286 pub pass: usize,
288 pub rule_applied: String,
290 pub before: String,
292 pub after: String,
294}
295
296#[derive(Debug, Clone, Default)]
302pub struct SesSimplifierStats {
303 pub simplify_calls: usize,
305 pub total_passes: usize,
307 pub total_rule_applications: usize,
309 pub fixpoint_reached: usize,
311 pub evaluate_calls: usize,
313 pub differentiate_calls: usize,
315 pub history_len: usize,
317}
318
319pub type SesSymbolicExpressionSimplifier = SymbolicExpressionSimplifier;
325
326pub struct SymbolicExpressionSimplifier {
333 rules: Vec<SesRewriteRule>,
335 history: VecDeque<SesSimplificationStep>,
337 config: SesSimplifierConfig,
339 stats: SesSimplifierStats,
341}
342
343impl SymbolicExpressionSimplifier {
344 pub fn new() -> Self {
350 let config = SesSimplifierConfig::default();
351 Self::with_config(config)
352 }
353
354 pub fn with_config(config: SesSimplifierConfig) -> Self {
356 let mut s = SymbolicExpressionSimplifier {
357 rules: Vec::new(),
358 history: VecDeque::with_capacity(500),
359 config,
360 stats: SesSimplifierStats::default(),
361 };
362 s.register_builtin_rules();
363 s
364 }
365
366 pub fn add_rule(&mut self, rule: SesRewriteRule) {
368 self.rules.push(rule);
369 }
370
371 fn register_builtin_rules(&mut self) {
376 self.rules.push(SesRewriteRule {
378 name: "add_zero_right".into(),
379 pattern: "x + 0 => x".into(),
380 applies: |e| matches!(e, SesExpr::Add(_, r) if is_zero(r)),
381 transform: Box::new(|e| {
382 if let SesExpr::Add(l, r) = e {
383 if is_zero(&r) {
384 return *l;
385 }
386 SesExpr::Add(l, r)
387 } else {
388 e
389 }
390 }),
391 });
392 self.rules.push(SesRewriteRule {
394 name: "add_zero_left".into(),
395 pattern: "0 + x => x".into(),
396 applies: |e| matches!(e, SesExpr::Add(l, _) if is_zero(l)),
397 transform: Box::new(|e| {
398 if let SesExpr::Add(l, r) = e {
399 if is_zero(&l) {
400 return *r;
401 }
402 SesExpr::Add(l, r)
403 } else {
404 e
405 }
406 }),
407 });
408 self.rules.push(SesRewriteRule {
410 name: "sub_zero".into(),
411 pattern: "x - 0 => x".into(),
412 applies: |e| matches!(e, SesExpr::Sub(_, r) if is_zero(r)),
413 transform: Box::new(|e| {
414 if let SesExpr::Sub(l, r) = e {
415 if is_zero(&r) {
416 return *l;
417 }
418 SesExpr::Sub(l, r)
419 } else {
420 e
421 }
422 }),
423 });
424 self.rules.push(SesRewriteRule {
426 name: "zero_sub".into(),
427 pattern: "0 - x => -x".into(),
428 applies: |e| matches!(e, SesExpr::Sub(l, _) if is_zero(l)),
429 transform: Box::new(|e| {
430 if let SesExpr::Sub(l, r) = e {
431 if is_zero(&l) {
432 return SesExpr::Neg(r);
433 }
434 SesExpr::Sub(l, r)
435 } else {
436 e
437 }
438 }),
439 });
440 self.rules.push(SesRewriteRule {
442 name: "mul_one_right".into(),
443 pattern: "x * 1 => x".into(),
444 applies: |e| matches!(e, SesExpr::Mul(_, r) if is_one(r)),
445 transform: Box::new(|e| {
446 if let SesExpr::Mul(l, r) = e {
447 if is_one(&r) {
448 return *l;
449 }
450 SesExpr::Mul(l, r)
451 } else {
452 e
453 }
454 }),
455 });
456 self.rules.push(SesRewriteRule {
458 name: "mul_one_left".into(),
459 pattern: "1 * x => x".into(),
460 applies: |e| matches!(e, SesExpr::Mul(l, _) if is_one(l)),
461 transform: Box::new(|e| {
462 if let SesExpr::Mul(l, r) = e {
463 if is_one(&l) {
464 return *r;
465 }
466 SesExpr::Mul(l, r)
467 } else {
468 e
469 }
470 }),
471 });
472 self.rules.push(SesRewriteRule {
474 name: "mul_zero_right".into(),
475 pattern: "x * 0 => 0".into(),
476 applies: |e| matches!(e, SesExpr::Mul(_, r) if is_zero(r)),
477 transform: Box::new(|e| {
478 if let SesExpr::Mul(_, r) = e {
479 if is_zero(&r) {
480 return SesExpr::Num(0.0);
481 }
482 SesExpr::Mul(Box::new(SesExpr::Num(0.0)), r)
483 } else {
484 e
485 }
486 }),
487 });
488 self.rules.push(SesRewriteRule {
490 name: "mul_zero_left".into(),
491 pattern: "0 * x => 0".into(),
492 applies: |e| matches!(e, SesExpr::Mul(l, _) if is_zero(l)),
493 transform: Box::new(|e| {
494 if let SesExpr::Mul(l, _) = e {
495 if is_zero(&l) {
496 return SesExpr::Num(0.0);
497 }
498 SesExpr::Mul(l, Box::new(SesExpr::Num(0.0)))
499 } else {
500 e
501 }
502 }),
503 });
504 self.rules.push(SesRewriteRule {
506 name: "div_one".into(),
507 pattern: "x / 1 => x".into(),
508 applies: |e| matches!(e, SesExpr::Div(_, r) if is_one(r)),
509 transform: Box::new(|e| {
510 if let SesExpr::Div(l, r) = e {
511 if is_one(&r) {
512 return *l;
513 }
514 SesExpr::Div(l, r)
515 } else {
516 e
517 }
518 }),
519 });
520 self.rules.push(SesRewriteRule {
522 name: "zero_div".into(),
523 pattern: "0 / x => 0 (x != 0)".into(),
524 applies: |e| matches!(e, SesExpr::Div(l, r) if is_zero(l) && !is_zero(r)),
525 transform: Box::new(|e| {
526 if let SesExpr::Div(l, r) = e {
527 if is_zero(&l) && !is_zero(&r) {
528 return SesExpr::Num(0.0);
529 }
530 SesExpr::Div(l, r)
531 } else {
532 e
533 }
534 }),
535 });
536 self.rules.push(SesRewriteRule {
538 name: "pow_one".into(),
539 pattern: "x ^ 1 => x".into(),
540 applies: |e| matches!(e, SesExpr::Pow(_, r) if is_one(r)),
541 transform: Box::new(|e| {
542 if let SesExpr::Pow(b, r) = e {
543 if is_one(&r) {
544 return *b;
545 }
546 SesExpr::Pow(b, r)
547 } else {
548 e
549 }
550 }),
551 });
552 self.rules.push(SesRewriteRule {
554 name: "pow_zero".into(),
555 pattern: "x ^ 0 => 1".into(),
556 applies: |e| matches!(e, SesExpr::Pow(_, r) if is_zero(r)),
557 transform: Box::new(|e| {
558 if let SesExpr::Pow(_, r) = e {
559 if is_zero(&r) {
560 return SesExpr::Num(1.0);
561 }
562 SesExpr::Pow(Box::new(SesExpr::Num(0.0)), r)
563 } else {
564 e
565 }
566 }),
567 });
568 self.rules.push(SesRewriteRule {
570 name: "one_pow".into(),
571 pattern: "1 ^ x => 1".into(),
572 applies: |e| matches!(e, SesExpr::Pow(b, _) if is_one(b)),
573 transform: Box::new(|e| {
574 if let SesExpr::Pow(b, _) = e {
575 if is_one(&b) {
576 return SesExpr::Num(1.0);
577 }
578 SesExpr::Pow(b, Box::new(SesExpr::Num(0.0)))
579 } else {
580 e
581 }
582 }),
583 });
584 self.rules.push(SesRewriteRule {
586 name: "double_neg".into(),
587 pattern: "--x => x".into(),
588 applies: |e| matches!(e, SesExpr::Neg(inner) if matches!(**inner, SesExpr::Neg(_))),
589 transform: Box::new(|e| {
590 if let SesExpr::Neg(inner) = e {
591 if let SesExpr::Neg(inner2) = *inner {
592 return *inner2;
593 }
594 SesExpr::Neg(inner)
595 } else {
596 e
597 }
598 }),
599 });
600 self.rules.push(SesRewriteRule {
602 name: "sub_self".into(),
603 pattern: "x - x => 0".into(),
604 applies: |e| matches!(e, SesExpr::Sub(l, r) if exprs_equal(l, r)),
605 transform: Box::new(|e| {
606 if let SesExpr::Sub(l, r) = e {
607 if exprs_equal(&l, &r) {
608 return SesExpr::Num(0.0);
609 }
610 SesExpr::Sub(l, r)
611 } else {
612 e
613 }
614 }),
615 });
616 self.rules.push(SesRewriteRule {
618 name: "div_self".into(),
619 pattern: "x / x => 1".into(),
620 applies: |e| matches!(e, SesExpr::Div(l, r) if exprs_equal(l, r) && !is_zero(l)),
621 transform: Box::new(|e| {
622 if let SesExpr::Div(l, r) = e {
623 if exprs_equal(&l, &r) && !is_zero(&l) {
624 return SesExpr::Num(1.0);
625 }
626 SesExpr::Div(l, r)
627 } else {
628 e
629 }
630 }),
631 });
632 self.rules.push(SesRewriteRule {
634 name: "sqrt_sq".into(),
635 pattern: "sqrt(x^2) => |x|".into(),
636 applies: |e| matches!(e, SesExpr::Sqrt(inner) if matches!(inner.as_ref(), SesExpr::Pow(_, exp) if is_two(exp))),
637 transform: Box::new(|e| {
638 if let SesExpr::Sqrt(inner) = e {
639 if let SesExpr::Pow(base, exp) = *inner {
640 if is_two(&exp) { return SesExpr::Abs(base); }
641 return SesExpr::Sqrt(Box::new(SesExpr::Pow(base, exp)));
642 }
643 SesExpr::Sqrt(inner)
644 } else { e }
645 }),
646 });
647 self.rules.push(SesRewriteRule {
649 name: "exp_ln".into(),
650 pattern: "exp(ln(x)) => x".into(),
651 applies: |e| matches!(e, SesExpr::Exp(inner) if matches!(**inner, SesExpr::Ln(_))),
652 transform: Box::new(|e| {
653 if let SesExpr::Exp(inner) = e {
654 if let SesExpr::Ln(x) = *inner {
655 return *x;
656 }
657 SesExpr::Exp(inner)
658 } else {
659 e
660 }
661 }),
662 });
663 self.rules.push(SesRewriteRule {
665 name: "ln_exp".into(),
666 pattern: "ln(exp(x)) => x".into(),
667 applies: |e| matches!(e, SesExpr::Ln(inner) if matches!(**inner, SesExpr::Exp(_))),
668 transform: Box::new(|e| {
669 if let SesExpr::Ln(inner) = e {
670 if let SesExpr::Exp(x) = *inner {
671 return *x;
672 }
673 SesExpr::Ln(inner)
674 } else {
675 e
676 }
677 }),
678 });
679 self.rules.push(SesRewriteRule {
681 name: "ln_one".into(),
682 pattern: "ln(1) => 0".into(),
683 applies: |e| matches!(e, SesExpr::Ln(inner) if is_one(inner)),
684 transform: Box::new(|e| {
685 if let SesExpr::Ln(inner) = e {
686 if is_one(&inner) {
687 return SesExpr::Num(0.0);
688 }
689 SesExpr::Ln(inner)
690 } else {
691 e
692 }
693 }),
694 });
695 self.rules.push(SesRewriteRule {
697 name: "exp_zero".into(),
698 pattern: "exp(0) => 1".into(),
699 applies: |e| matches!(e, SesExpr::Exp(inner) if is_zero(inner)),
700 transform: Box::new(|e| {
701 if let SesExpr::Exp(inner) = e {
702 if is_zero(&inner) {
703 return SesExpr::Num(1.0);
704 }
705 SesExpr::Exp(inner)
706 } else {
707 e
708 }
709 }),
710 });
711 self.rules.push(SesRewriteRule {
713 name: "sqrt_zero".into(),
714 pattern: "sqrt(0) => 0".into(),
715 applies: |e| matches!(e, SesExpr::Sqrt(inner) if is_zero(inner)),
716 transform: Box::new(|e| {
717 if let SesExpr::Sqrt(inner) = e {
718 if is_zero(&inner) {
719 return SesExpr::Num(0.0);
720 }
721 SesExpr::Sqrt(inner)
722 } else {
723 e
724 }
725 }),
726 });
727 self.rules.push(SesRewriteRule {
729 name: "sqrt_one".into(),
730 pattern: "sqrt(1) => 1".into(),
731 applies: |e| matches!(e, SesExpr::Sqrt(inner) if is_one(inner)),
732 transform: Box::new(|e| {
733 if let SesExpr::Sqrt(inner) = e {
734 if is_one(&inner) {
735 return SesExpr::Num(1.0);
736 }
737 SesExpr::Sqrt(inner)
738 } else {
739 e
740 }
741 }),
742 });
743 self.rules.push(SesRewriteRule {
745 name: "neg_zero".into(),
746 pattern: "-0 => 0".into(),
747 applies: |e| matches!(e, SesExpr::Neg(inner) if is_zero(inner)),
748 transform: Box::new(|e| {
749 if let SesExpr::Neg(inner) = e {
750 if is_zero(&inner) {
751 return SesExpr::Num(0.0);
752 }
753 SesExpr::Neg(inner)
754 } else {
755 e
756 }
757 }),
758 });
759 self.rules.push(SesRewriteRule {
761 name: "sin2_cos2".into(),
762 pattern: "sin(x)^2 + cos(x)^2 => 1".into(),
763 applies: |e| is_sin2_plus_cos2(e),
764 transform: Box::new(|e| {
765 if is_sin2_plus_cos2(&e) {
766 SesExpr::Num(1.0)
767 } else {
768 e
769 }
770 }),
771 });
772 self.rules.push(SesRewriteRule {
774 name: "mul_self_to_pow2".into(),
775 pattern: "x * x => x^2".into(),
776 applies: |e| matches!(e, SesExpr::Mul(l, r) if exprs_equal(l, r) && !matches!(**l, SesExpr::Num(_))),
777 transform: Box::new(|e| {
778 if let SesExpr::Mul(l, r) = e {
779 if exprs_equal(&l, &r) && !matches!(*l, SesExpr::Num(_)) {
780 return SesExpr::Pow(l, Box::new(SesExpr::Num(2.0)));
781 }
782 SesExpr::Mul(l, r)
783 } else { e }
784 }),
785 });
786 self.rules.push(SesRewriteRule {
788 name: "add_self".into(),
789 pattern: "x + x => 2*x".into(),
790 applies: |e| matches!(e, SesExpr::Add(l, r) if exprs_equal(l, r) && !matches!(**l, SesExpr::Num(_))),
791 transform: Box::new(|e| {
792 if let SesExpr::Add(l, r) = e {
793 if exprs_equal(&l, &r) && !matches!(*l, SesExpr::Num(_)) {
794 return SesExpr::Mul(Box::new(SesExpr::Num(2.0)), l);
795 }
796 SesExpr::Add(l, r)
797 } else { e }
798 }),
799 });
800 }
801
802 pub fn simplify(&mut self, expr: &SesExpr) -> SesExpr {
808 self.stats.simplify_calls += 1;
809 let mut current = expr.clone();
810 let mut pass = 0;
811 let max = self.config.max_passes;
812 loop {
813 let before_pass = current.clone();
814 current = self.one_pass(current, pass);
815 pass += 1;
816 self.stats.total_passes += 1;
817 if exprs_equal(¤t, &before_pass) {
818 self.stats.fixpoint_reached += 1;
819 break;
820 }
821 if pass >= max {
822 break;
823 }
824 }
825 current
826 }
827
828 fn one_pass(&mut self, expr: SesExpr, pass: usize) -> SesExpr {
830 let expr = self.recurse_children(expr, pass);
832 let expr = if self.config.enable_constant_folding {
834 self.fold_constants(expr, pass)
835 } else {
836 expr
837 };
838 self.apply_rules(expr, pass)
840 }
841
842 fn recurse_children(&mut self, expr: SesExpr, pass: usize) -> SesExpr {
844 match expr {
845 SesExpr::Add(l, r) => SesExpr::Add(
846 Box::new(self.one_pass(*l, pass)),
847 Box::new(self.one_pass(*r, pass)),
848 ),
849 SesExpr::Sub(l, r) => SesExpr::Sub(
850 Box::new(self.one_pass(*l, pass)),
851 Box::new(self.one_pass(*r, pass)),
852 ),
853 SesExpr::Mul(l, r) => SesExpr::Mul(
854 Box::new(self.one_pass(*l, pass)),
855 Box::new(self.one_pass(*r, pass)),
856 ),
857 SesExpr::Div(l, r) => SesExpr::Div(
858 Box::new(self.one_pass(*l, pass)),
859 Box::new(self.one_pass(*r, pass)),
860 ),
861 SesExpr::Pow(b, e) => SesExpr::Pow(
862 Box::new(self.one_pass(*b, pass)),
863 Box::new(self.one_pass(*e, pass)),
864 ),
865 SesExpr::Neg(inner) => SesExpr::Neg(Box::new(self.one_pass(*inner, pass))),
866 SesExpr::Abs(inner) => SesExpr::Abs(Box::new(self.one_pass(*inner, pass))),
867 SesExpr::Sqrt(inner) => SesExpr::Sqrt(Box::new(self.one_pass(*inner, pass))),
868 SesExpr::Sin(inner) => SesExpr::Sin(Box::new(self.one_pass(*inner, pass))),
869 SesExpr::Cos(inner) => SesExpr::Cos(Box::new(self.one_pass(*inner, pass))),
870 SesExpr::Exp(inner) => SesExpr::Exp(Box::new(self.one_pass(*inner, pass))),
871 SesExpr::Ln(inner) => SesExpr::Ln(Box::new(self.one_pass(*inner, pass))),
872 atom => atom,
873 }
874 }
875
876 fn fold_constants(&mut self, expr: SesExpr, pass: usize) -> SesExpr {
878 if !is_constant(&expr) {
879 return expr;
880 }
881 let vars: HashMap<String, f64> = HashMap::new();
883 if let Ok(v) = self.evaluate_inner(&expr, &vars) {
884 if v.is_finite() {
885 let before = expr.to_string();
886 let folded = SesExpr::Num(v);
887 self.record_step(pass, "constant_folding", &before, &folded.to_string());
888 return folded;
889 }
890 }
891 expr
892 }
893
894 fn apply_rules(&mut self, expr: SesExpr, pass: usize) -> SesExpr {
896 let applicable: Vec<usize> = self
898 .rules
899 .iter()
900 .enumerate()
901 .filter(|(_, r)| {
902 let skip = (r.name.starts_with("sin2_cos2") && !self.config.enable_trig_rules)
903 || (!self.config.enable_algebraic_rules && !r.name.starts_with("constant"));
904 if skip {
905 return false;
906 }
907 (r.applies)(&expr)
908 })
909 .map(|(i, _)| i)
910 .collect();
911
912 if let Some(&idx) = applicable.first() {
913 let rule_name = self.rules[idx].name.clone();
914 let before = expr.to_string();
915 let result = (self.rules[idx].transform)(expr);
916 let after = result.to_string();
917 if before != after {
918 self.stats.total_rule_applications += 1;
919 self.record_step(pass, &rule_name, &before, &after);
920 }
921 result
922 } else {
923 expr
924 }
925 }
926
927 fn record_step(&mut self, pass: usize, rule: &str, before: &str, after: &str) {
928 if self.history.len() >= 500 {
929 self.history.pop_front();
930 }
931 self.history.push_back(SesSimplificationStep {
932 pass,
933 rule_applied: rule.to_string(),
934 before: before.to_string(),
935 after: after.to_string(),
936 });
937 self.stats.history_len = self.history.len();
938 }
939
940 pub fn evaluate(
946 &mut self,
947 expr: &SesExpr,
948 vars: &HashMap<String, f64>,
949 ) -> Result<f64, SesError> {
950 self.stats.evaluate_calls += 1;
951 self.evaluate_inner(expr, vars)
952 }
953
954 fn evaluate_inner(&self, expr: &SesExpr, vars: &HashMap<String, f64>) -> Result<f64, SesError> {
955 match expr {
956 SesExpr::Num(v) => Ok(*v),
957 SesExpr::Var(name) => vars
958 .get(name.as_str())
959 .copied()
960 .ok_or_else(|| SesError::UnboundVariable(name.clone())),
961 SesExpr::Add(l, r) => Ok(self.evaluate_inner(l, vars)? + self.evaluate_inner(r, vars)?),
962 SesExpr::Sub(l, r) => Ok(self.evaluate_inner(l, vars)? - self.evaluate_inner(r, vars)?),
963 SesExpr::Mul(l, r) => Ok(self.evaluate_inner(l, vars)? * self.evaluate_inner(r, vars)?),
964 SesExpr::Div(l, r) => {
965 let denom = self.evaluate_inner(r, vars)?;
966 if denom == 0.0 {
967 return Err(SesError::DivisionByZero);
968 }
969 Ok(self.evaluate_inner(l, vars)? / denom)
970 }
971 SesExpr::Pow(b, e) => {
972 let base = self.evaluate_inner(b, vars)?;
973 let exp = self.evaluate_inner(e, vars)?;
974 Ok(base.powf(exp))
975 }
976 SesExpr::Neg(inner) => Ok(-self.evaluate_inner(inner, vars)?),
977 SesExpr::Abs(inner) => Ok(self.evaluate_inner(inner, vars)?.abs()),
978 SesExpr::Sqrt(inner) => {
979 let v = self.evaluate_inner(inner, vars)?;
980 if v < 0.0 {
981 return Err(SesError::MathDomainError("sqrt of negative number".into()));
982 }
983 Ok(v.sqrt())
984 }
985 SesExpr::Sin(inner) => Ok(self.evaluate_inner(inner, vars)?.sin()),
986 SesExpr::Cos(inner) => Ok(self.evaluate_inner(inner, vars)?.cos()),
987 SesExpr::Exp(inner) => Ok(self.evaluate_inner(inner, vars)?.exp()),
988 SesExpr::Ln(inner) => {
989 let v = self.evaluate_inner(inner, vars)?;
990 if v <= 0.0 {
991 return Err(SesError::MathDomainError(
992 "ln of non-positive number".into(),
993 ));
994 }
995 Ok(v.ln())
996 }
997 }
998 }
999
1000 pub fn differentiate(&mut self, expr: &SesExpr, var: &str) -> SesExpr {
1006 self.stats.differentiate_calls += 1;
1007 self.diff(expr, var)
1008 }
1009
1010 fn diff(&self, expr: &SesExpr, var: &str) -> SesExpr {
1011 match expr {
1012 SesExpr::Num(_) => SesExpr::Num(0.0),
1013 SesExpr::Var(name) => {
1014 if name == var {
1015 SesExpr::Num(1.0)
1016 } else {
1017 SesExpr::Num(0.0)
1018 }
1019 }
1020 SesExpr::Add(l, r) => SesExpr::add(self.diff(l, var), self.diff(r, var)),
1022 SesExpr::Sub(l, r) => SesExpr::sub(self.diff(l, var), self.diff(r, var)),
1024 SesExpr::Mul(l, r) => SesExpr::add(
1026 SesExpr::mul(self.diff(l, var), *r.clone()),
1027 SesExpr::mul(*l.clone(), self.diff(r, var)),
1028 ),
1029 SesExpr::Div(l, r) => {
1031 let f_prime = self.diff(l, var);
1032 let g_prime = self.diff(r, var);
1033 let num = SesExpr::sub(
1034 SesExpr::mul(f_prime, *r.clone()),
1035 SesExpr::mul(*l.clone(), g_prime),
1036 );
1037 let denom = SesExpr::pow(*r.clone(), SesExpr::Num(2.0));
1038 SesExpr::div(num, denom)
1039 }
1040 SesExpr::Pow(base, exp) => {
1043 let base_dep = self.contains_var(base, var);
1044 let exp_dep = self.contains_var(exp, var);
1045 match (base_dep, exp_dep) {
1046 (false, false) => SesExpr::Num(0.0),
1047 (true, false) => {
1049 let n = *exp.clone();
1050 let n_minus_1 = SesExpr::sub(n.clone(), SesExpr::Num(1.0));
1051 let chain = self.diff(base, var);
1052 SesExpr::mul(
1053 SesExpr::mul(n, SesExpr::pow(*base.clone(), n_minus_1)),
1054 chain,
1055 )
1056 }
1057 (false, true) => {
1059 let chain = self.diff(exp, var);
1060 SesExpr::mul(
1061 SesExpr::mul(
1062 SesExpr::pow(*base.clone(), *exp.clone()),
1063 SesExpr::Ln(base.clone()),
1064 ),
1065 chain,
1066 )
1067 }
1068 (true, true) => {
1070 let f_prime = self.diff(base, var);
1071 let g_prime = self.diff(exp, var);
1072 let term1 = SesExpr::mul(g_prime, SesExpr::Ln(base.clone()));
1073 let term2 =
1074 SesExpr::mul(*exp.clone(), SesExpr::div(f_prime, *base.clone()));
1075 SesExpr::mul(
1076 SesExpr::pow(*base.clone(), *exp.clone()),
1077 SesExpr::add(term1, term2),
1078 )
1079 }
1080 }
1081 }
1082 SesExpr::Neg(inner) => SesExpr::neg(self.diff(inner, var)),
1084 SesExpr::Abs(inner) => {
1086 let f_prime = self.diff(inner, var);
1087 SesExpr::div(
1088 SesExpr::mul(*inner.clone(), f_prime),
1089 SesExpr::Abs(inner.clone()),
1090 )
1091 }
1092 SesExpr::Sqrt(inner) => {
1094 let f_prime = self.diff(inner, var);
1095 SesExpr::div(
1096 f_prime,
1097 SesExpr::mul(SesExpr::Num(2.0), SesExpr::Sqrt(inner.clone())),
1098 )
1099 }
1100 SesExpr::Sin(inner) => {
1102 let f_prime = self.diff(inner, var);
1103 SesExpr::mul(SesExpr::Cos(inner.clone()), f_prime)
1104 }
1105 SesExpr::Cos(inner) => {
1107 let f_prime = self.diff(inner, var);
1108 SesExpr::neg(SesExpr::mul(SesExpr::Sin(inner.clone()), f_prime))
1109 }
1110 SesExpr::Exp(inner) => {
1112 let f_prime = self.diff(inner, var);
1113 SesExpr::mul(SesExpr::Exp(inner.clone()), f_prime)
1114 }
1115 SesExpr::Ln(inner) => {
1117 let f_prime = self.diff(inner, var);
1118 SesExpr::div(f_prime, *inner.clone())
1119 }
1120 }
1121 }
1122
1123 pub fn substitute(&self, expr: &SesExpr, var: &str, replacement: &SesExpr) -> SesExpr {
1129 match expr {
1130 SesExpr::Var(name) if name == var => replacement.clone(),
1131 SesExpr::Num(_) | SesExpr::Var(_) => expr.clone(),
1132 SesExpr::Add(l, r) => SesExpr::add(
1133 self.substitute(l, var, replacement),
1134 self.substitute(r, var, replacement),
1135 ),
1136 SesExpr::Sub(l, r) => SesExpr::sub(
1137 self.substitute(l, var, replacement),
1138 self.substitute(r, var, replacement),
1139 ),
1140 SesExpr::Mul(l, r) => SesExpr::mul(
1141 self.substitute(l, var, replacement),
1142 self.substitute(r, var, replacement),
1143 ),
1144 SesExpr::Div(l, r) => SesExpr::div(
1145 self.substitute(l, var, replacement),
1146 self.substitute(r, var, replacement),
1147 ),
1148 SesExpr::Pow(b, e) => SesExpr::pow(
1149 self.substitute(b, var, replacement),
1150 self.substitute(e, var, replacement),
1151 ),
1152 SesExpr::Neg(inner) => SesExpr::neg(self.substitute(inner, var, replacement)),
1153 SesExpr::Abs(inner) => SesExpr::Abs(Box::new(self.substitute(inner, var, replacement))),
1154 SesExpr::Sqrt(inner) => {
1155 SesExpr::Sqrt(Box::new(self.substitute(inner, var, replacement)))
1156 }
1157 SesExpr::Sin(inner) => SesExpr::Sin(Box::new(self.substitute(inner, var, replacement))),
1158 SesExpr::Cos(inner) => SesExpr::Cos(Box::new(self.substitute(inner, var, replacement))),
1159 SesExpr::Exp(inner) => SesExpr::Exp(Box::new(self.substitute(inner, var, replacement))),
1160 SesExpr::Ln(inner) => SesExpr::Ln(Box::new(self.substitute(inner, var, replacement))),
1161 }
1162 }
1163
1164 pub fn contains_var(&self, expr: &SesExpr, var: &str) -> bool {
1170 match expr {
1171 SesExpr::Var(name) => name == var,
1172 SesExpr::Num(_) => false,
1173 SesExpr::Add(l, r)
1174 | SesExpr::Sub(l, r)
1175 | SesExpr::Mul(l, r)
1176 | SesExpr::Div(l, r)
1177 | SesExpr::Pow(l, r) => self.contains_var(l, var) || self.contains_var(r, var),
1178 SesExpr::Neg(i)
1179 | SesExpr::Abs(i)
1180 | SesExpr::Sqrt(i)
1181 | SesExpr::Sin(i)
1182 | SesExpr::Cos(i)
1183 | SesExpr::Exp(i)
1184 | SesExpr::Ln(i) => self.contains_var(i, var),
1185 }
1186 }
1187
1188 pub fn collect_vars(&self, expr: &SesExpr) -> HashSet<String> {
1190 let mut set = HashSet::new();
1191 collect_vars_inner(expr, &mut set);
1192 set
1193 }
1194
1195 pub fn to_string(&self, expr: &SesExpr) -> String {
1201 expr.to_string()
1202 }
1203
1204 pub fn parse(s: &str) -> Result<SesExpr, SesError> {
1214 let tokens = tokenize(s)?;
1215 let mut parser = Parser::new(tokens);
1216 let expr = parser.parse_expr()?;
1217 if !parser.is_at_end() {
1218 return Err(SesError::ParseError(format!(
1219 "unexpected token at position {}",
1220 parser.pos
1221 )));
1222 }
1223 Ok(expr)
1224 }
1225
1226 pub fn simplifier_stats(&self) -> SesSimplifierStats {
1232 let mut stats = self.stats.clone();
1233 stats.history_len = self.history.len();
1234 stats
1235 }
1236
1237 pub fn history(&self) -> &VecDeque<SesSimplificationStep> {
1239 &self.history
1240 }
1241
1242 pub fn clear_history(&mut self) {
1244 self.history.clear();
1245 self.stats.history_len = 0;
1246 }
1247
1248 pub fn config(&self) -> &SesSimplifierConfig {
1250 &self.config
1251 }
1252
1253 pub fn rule_count(&self) -> usize {
1255 self.rules.len()
1256 }
1257}
1258
1259impl Default for SymbolicExpressionSimplifier {
1260 fn default() -> Self {
1261 Self::new()
1262 }
1263}
1264
1265fn is_zero(e: &SesExpr) -> bool {
1270 matches!(e, SesExpr::Num(v) if *v == 0.0)
1271}
1272
1273fn is_one(e: &SesExpr) -> bool {
1274 matches!(e, SesExpr::Num(v) if *v == 1.0)
1275}
1276
1277fn is_two(e: &SesExpr) -> bool {
1278 matches!(e, SesExpr::Num(v) if *v == 2.0)
1279}
1280
1281fn is_constant(e: &SesExpr) -> bool {
1283 match e {
1284 SesExpr::Num(_) => true,
1285 SesExpr::Var(_) => false,
1286 SesExpr::Add(l, r)
1287 | SesExpr::Sub(l, r)
1288 | SesExpr::Mul(l, r)
1289 | SesExpr::Div(l, r)
1290 | SesExpr::Pow(l, r) => is_constant(l) && is_constant(r),
1291 SesExpr::Neg(i)
1292 | SesExpr::Abs(i)
1293 | SesExpr::Sqrt(i)
1294 | SesExpr::Sin(i)
1295 | SesExpr::Cos(i)
1296 | SesExpr::Exp(i)
1297 | SesExpr::Ln(i) => is_constant(i),
1298 }
1299}
1300
1301fn exprs_equal(a: &SesExpr, b: &SesExpr) -> bool {
1303 a == b
1304}
1305
1306fn is_sin2_plus_cos2(e: &SesExpr) -> bool {
1308 let SesExpr::Add(l, r) = e else {
1309 return false;
1310 };
1311 is_sin_squared(l) && is_cos_squared(r) || is_cos_squared(l) && is_sin_squared(r)
1312}
1313
1314fn is_sin_squared(e: &SesExpr) -> bool {
1315 if let SesExpr::Pow(base, exp) = e {
1316 is_two(exp) && matches!(base.as_ref(), SesExpr::Sin(_))
1317 } else {
1318 false
1319 }
1320}
1321
1322fn is_cos_squared(e: &SesExpr) -> bool {
1323 if let SesExpr::Pow(base, exp) = e {
1324 is_two(exp) && matches!(base.as_ref(), SesExpr::Cos(_))
1325 } else {
1326 false
1327 }
1328}
1329
1330fn collect_vars_inner(e: &SesExpr, set: &mut HashSet<String>) {
1331 match e {
1332 SesExpr::Var(name) => {
1333 set.insert(name.clone());
1334 }
1335 SesExpr::Num(_) => {}
1336 SesExpr::Add(l, r)
1337 | SesExpr::Sub(l, r)
1338 | SesExpr::Mul(l, r)
1339 | SesExpr::Div(l, r)
1340 | SesExpr::Pow(l, r) => {
1341 collect_vars_inner(l, set);
1342 collect_vars_inner(r, set);
1343 }
1344 SesExpr::Neg(i)
1345 | SesExpr::Abs(i)
1346 | SesExpr::Sqrt(i)
1347 | SesExpr::Sin(i)
1348 | SesExpr::Cos(i)
1349 | SesExpr::Exp(i)
1350 | SesExpr::Ln(i) => collect_vars_inner(i, set),
1351 }
1352}
1353
1354#[derive(Debug, Clone, PartialEq)]
1359enum Token {
1360 Num(f64),
1361 Ident(String),
1362 Plus,
1363 Minus,
1364 Star,
1365 Slash,
1366 Caret,
1367 LParen,
1368 RParen,
1369 Pipe,
1370}
1371
1372fn tokenize(s: &str) -> Result<Vec<Token>, SesError> {
1373 let mut tokens = Vec::new();
1374 let chars: Vec<char> = s.chars().collect();
1375 let mut i = 0;
1376 while i < chars.len() {
1377 let c = chars[i];
1378 if c.is_whitespace() {
1379 i += 1;
1380 continue;
1381 }
1382 if c.is_ascii_digit() || (c == '.' && i + 1 < chars.len() && chars[i + 1].is_ascii_digit())
1383 {
1384 let start = i;
1385 while i < chars.len() && (chars[i].is_ascii_digit() || chars[i] == '.') {
1386 i += 1;
1387 }
1388 if i < chars.len() && (chars[i] == 'e' || chars[i] == 'E') {
1390 i += 1;
1391 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
1392 i += 1;
1393 }
1394 while i < chars.len() && chars[i].is_ascii_digit() {
1395 i += 1;
1396 }
1397 }
1398 let num_str: String = chars[start..i].iter().collect();
1399 let v = num_str
1400 .parse::<f64>()
1401 .map_err(|_| SesError::ParseError(format!("invalid number: {num_str}")))?;
1402 tokens.push(Token::Num(v));
1403 } else if c.is_alphabetic() || c == '_' {
1404 let start = i;
1405 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
1406 i += 1;
1407 }
1408 let ident: String = chars[start..i].iter().collect();
1409 tokens.push(Token::Ident(ident));
1410 } else {
1411 let tok = match c {
1412 '+' => Token::Plus,
1413 '-' => Token::Minus,
1414 '*' => Token::Star,
1415 '/' => Token::Slash,
1416 '^' => Token::Caret,
1417 '(' => Token::LParen,
1418 ')' => Token::RParen,
1419 '|' => Token::Pipe,
1420 _ => return Err(SesError::ParseError(format!("unexpected character: '{c}'"))),
1421 };
1422 tokens.push(tok);
1423 i += 1;
1424 }
1425 }
1426 Ok(tokens)
1427}
1428
1429struct Parser {
1434 tokens: Vec<Token>,
1435 pos: usize,
1436}
1437
1438impl Parser {
1439 fn new(tokens: Vec<Token>) -> Self {
1440 Parser { tokens, pos: 0 }
1441 }
1442
1443 fn peek(&self) -> Option<&Token> {
1444 self.tokens.get(self.pos)
1445 }
1446
1447 fn consume(&mut self) -> Option<Token> {
1448 if self.pos < self.tokens.len() {
1449 let t = self.tokens[self.pos].clone();
1450 self.pos += 1;
1451 Some(t)
1452 } else {
1453 None
1454 }
1455 }
1456
1457 fn expect(&mut self, expected: &Token) -> Result<(), SesError> {
1458 match self.consume() {
1459 Some(ref t) if t == expected => Ok(()),
1460 Some(t) => Err(SesError::ParseError(format!(
1461 "expected {expected:?}, got {t:?}"
1462 ))),
1463 None => Err(SesError::ParseError(format!(
1464 "expected {expected:?}, got EOF"
1465 ))),
1466 }
1467 }
1468
1469 fn is_at_end(&self) -> bool {
1470 self.pos >= self.tokens.len()
1471 }
1472
1473 fn parse_expr(&mut self) -> Result<SesExpr, SesError> {
1475 self.parse_additive()
1476 }
1477
1478 fn parse_additive(&mut self) -> Result<SesExpr, SesError> {
1480 let mut lhs = self.parse_multiplicative()?;
1481 loop {
1482 match self.peek() {
1483 Some(Token::Plus) => {
1484 self.consume();
1485 let rhs = self.parse_multiplicative()?;
1486 lhs = SesExpr::add(lhs, rhs);
1487 }
1488 Some(Token::Minus) => {
1489 self.consume();
1490 let rhs = self.parse_multiplicative()?;
1491 lhs = SesExpr::sub(lhs, rhs);
1492 }
1493 _ => break,
1494 }
1495 }
1496 Ok(lhs)
1497 }
1498
1499 fn parse_multiplicative(&mut self) -> Result<SesExpr, SesError> {
1501 let mut lhs = self.parse_power()?;
1502 loop {
1503 match self.peek() {
1504 Some(Token::Star) => {
1505 self.consume();
1506 let rhs = self.parse_power()?;
1507 lhs = SesExpr::mul(lhs, rhs);
1508 }
1509 Some(Token::Slash) => {
1510 self.consume();
1511 let rhs = self.parse_power()?;
1512 lhs = SesExpr::div(lhs, rhs);
1513 }
1514 _ => break,
1515 }
1516 }
1517 Ok(lhs)
1518 }
1519
1520 fn parse_power(&mut self) -> Result<SesExpr, SesError> {
1522 let base = self.parse_unary()?;
1523 if matches!(self.peek(), Some(Token::Caret)) {
1524 self.consume();
1525 let exp = self.parse_power()?; Ok(SesExpr::pow(base, exp))
1527 } else {
1528 Ok(base)
1529 }
1530 }
1531
1532 fn parse_unary(&mut self) -> Result<SesExpr, SesError> {
1534 if matches!(self.peek(), Some(Token::Minus)) {
1535 self.consume();
1536 let inner = self.parse_unary()?;
1537 Ok(SesExpr::neg(inner))
1538 } else {
1539 self.parse_atom()
1540 }
1541 }
1542
1543 fn parse_atom(&mut self) -> Result<SesExpr, SesError> {
1545 match self.peek().cloned() {
1546 Some(Token::Num(v)) => {
1547 self.consume();
1548 Ok(SesExpr::Num(v))
1549 }
1550 Some(Token::Ident(name)) => {
1551 self.consume();
1552 if matches!(self.peek(), Some(Token::LParen)) {
1554 self.consume(); let arg = self.parse_expr()?;
1556 self.expect(&Token::RParen)?;
1557 match name.to_lowercase().as_str() {
1558 "sin" => Ok(SesExpr::Sin(Box::new(arg))),
1559 "cos" => Ok(SesExpr::Cos(Box::new(arg))),
1560 "exp" => Ok(SesExpr::Exp(Box::new(arg))),
1561 "ln" | "log" => Ok(SesExpr::Ln(Box::new(arg))),
1562 "sqrt" => Ok(SesExpr::Sqrt(Box::new(arg))),
1563 "abs" => Ok(SesExpr::Abs(Box::new(arg))),
1564 "neg" => Ok(SesExpr::neg(arg)),
1565 other => Err(SesError::ParseError(format!("unknown function: {other}"))),
1566 }
1567 } else {
1568 Ok(SesExpr::Var(name))
1569 }
1570 }
1571 Some(Token::LParen) => {
1572 self.consume();
1573 let inner = self.parse_expr()?;
1574 self.expect(&Token::RParen)?;
1575 Ok(inner)
1576 }
1577 Some(Token::Pipe) => {
1578 self.consume();
1579 let inner = self.parse_expr()?;
1580 self.expect(&Token::Pipe)?;
1581 Ok(SesExpr::Abs(Box::new(inner)))
1582 }
1583 Some(t) => Err(SesError::ParseError(format!("unexpected token: {t:?}"))),
1584 None => Err(SesError::ParseError("unexpected end of input".into())),
1585 }
1586 }
1587}
1588
1589#[cfg(test)]
1594#[path = "ses_tests.rs"]
1595mod tests;