clifford_codegen/symbolic/
simplify.rs1use symbolica::atom::{Atom, AtomCore, AtomView};
7use symbolica::coefficient::CoefficientView;
8
9pub struct ExpressionSimplifier;
14
15impl ExpressionSimplifier {
16 pub fn new() -> Self {
18 Self
19 }
20
21 pub fn simplify(&self, expr: &Atom) -> Atom {
37 expr.expand()
38 }
39
40 pub fn is_zero(&self, expr: &Atom) -> bool {
44 let expanded = expr.expand();
45 match expanded.as_atom_view() {
46 AtomView::Num(n) => {
47 let coeff = n.get_coeff_view();
48 matches!(coeff, CoefficientView::Natural(0, _, 0, _))
49 }
50 _ => false,
51 }
52 }
53
54 pub fn term_count(&self, expr: &Atom) -> usize {
58 let expanded = expr.expand();
59 match expanded.as_atom_view() {
60 AtomView::Add(a) => a.iter().count(),
61 AtomView::Num(n) => {
62 let coeff = n.get_coeff_view();
63 if matches!(coeff, CoefficientView::Natural(0, _, 0, _)) {
64 0
65 } else {
66 1
67 }
68 }
69 _ => 1,
70 }
71 }
72
73 pub fn operation_count(&self, expr: &Atom) -> usize {
77 self.count_ops_view(expr.as_atom_view())
78 }
79
80 fn count_ops_view(&self, view: AtomView<'_>) -> usize {
82 match view {
83 AtomView::Num(_) | AtomView::Var(_) => 0,
84 AtomView::Add(a) => {
85 let children: usize = a.iter().map(|c| self.count_ops_view(c)).sum();
86 let additions = a.iter().count().saturating_sub(1);
87 children + additions
88 }
89 AtomView::Mul(m) => {
90 let children: usize = m.iter().map(|c| self.count_ops_view(c)).sum();
91 let multiplications = m.iter().count().saturating_sub(1);
92 children + multiplications
93 }
94 AtomView::Pow(p) => {
95 let (base, exp) = p.get_base_exp();
96 self.count_ops_view(base) + self.count_ops_view(exp) + 1
97 }
98 AtomView::Fun(f) => {
99 let children: usize = f.iter().map(|c| self.count_ops_view(c)).sum();
100 children + 1
101 }
102 }
103 }
104}
105
106impl Default for ExpressionSimplifier {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use std::sync::Mutex;
116
117 static SYMBOLICA_LOCK: Mutex<()> = Mutex::new(());
121
122 #[test]
123 fn symbolica_simplify_expands_product() {
124 let _guard = SYMBOLICA_LOCK.lock().unwrap();
125 let simplifier = ExpressionSimplifier::new();
126
127 let one = Atom::num(1);
129 let sum = &one + &one;
130 let simplified = simplifier.simplify(&sum);
131
132 assert!(!simplifier.is_zero(&simplified));
134 }
135
136 #[test]
137 fn symbolica_is_zero_detects_zero() {
138 let _guard = SYMBOLICA_LOCK.lock().unwrap();
139 let simplifier = ExpressionSimplifier::new();
140
141 let zero = Atom::num(0);
142 assert!(simplifier.is_zero(&zero));
143
144 let one = Atom::num(1);
145 assert!(!simplifier.is_zero(&one));
146 }
147
148 #[test]
149 fn symbolica_term_count_for_sum() {
150 let _guard = SYMBOLICA_LOCK.lock().unwrap();
151 let simplifier = ExpressionSimplifier::new();
152
153 let a = Atom::num(1);
155 let b = Atom::num(2);
156 let sum = &a + &b;
157
158 assert_eq!(simplifier.term_count(&sum), 1);
160 }
161}