use crate::kernel::expr::ExprData;
use crate::kernel::pool::ExprPool;
use crate::kernel::ExprId;
pub fn mult_tree_is_commutative(pool: &ExprPool, expr: ExprId) -> bool {
pool.is_mult_commutative(expr)
}
pub fn expr_contains_noncommutative_symbol(pool: &ExprPool, expr: ExprId) -> bool {
pool.with(expr, |data| match data {
ExprData::Symbol { commutative, .. } => !*commutative,
ExprData::Integer(_) | ExprData::Rational(_) | ExprData::Float(_) => false,
ExprData::Add(args) | ExprData::Mul(args) => args
.iter()
.any(|&c| expr_contains_noncommutative_symbol(pool, c)),
ExprData::Pow { base, exp } => {
expr_contains_noncommutative_symbol(pool, *base)
|| expr_contains_noncommutative_symbol(pool, *exp)
}
ExprData::Func { args, .. } => args
.iter()
.any(|&c| expr_contains_noncommutative_symbol(pool, c)),
ExprData::Piecewise { branches, default } => {
branches.iter().any(|(c, v)| {
expr_contains_noncommutative_symbol(pool, *c)
|| expr_contains_noncommutative_symbol(pool, *v)
}) || expr_contains_noncommutative_symbol(pool, *default)
}
ExprData::Predicate { args, .. } => args
.iter()
.any(|&c| expr_contains_noncommutative_symbol(pool, c)),
ExprData::Forall { var, body } | ExprData::Exists { var, body } => {
expr_contains_noncommutative_symbol(pool, *var)
|| expr_contains_noncommutative_symbol(pool, *body)
}
ExprData::BigO(inner) => expr_contains_noncommutative_symbol(pool, *inner),
ExprData::RootSum { poly, body, .. } => {
expr_contains_noncommutative_symbol(pool, *poly)
|| expr_contains_noncommutative_symbol(pool, *body)
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernel::Domain;
fn reference(pool: &ExprPool, expr: ExprId) -> bool {
pool.with(expr, |data| match data {
ExprData::Symbol { commutative, .. } => *commutative,
ExprData::Integer(_) | ExprData::Rational(_) | ExprData::Float(_) => true,
ExprData::Add(args) | ExprData::Mul(args) => args.iter().all(|&c| reference(pool, c)),
ExprData::Pow { base, exp } => reference(pool, *base) && reference(pool, *exp),
ExprData::Func { args, .. } => args.iter().all(|&c| reference(pool, c)),
ExprData::Piecewise { branches, default } => {
branches
.iter()
.all(|(c, v)| reference(pool, *c) && reference(pool, *v))
&& reference(pool, *default)
}
ExprData::Predicate { args, .. } => args.iter().all(|&c| reference(pool, c)),
ExprData::Forall { var, body } | ExprData::Exists { var, body } => {
reference(pool, *var) && reference(pool, *body)
}
ExprData::BigO(inner) => reference(pool, *inner),
ExprData::RootSum { poly, body, .. } => {
reference(pool, *poly) && reference(pool, *body)
}
})
}
#[test]
fn cached_flag_matches_full_walk() {
let pool = ExprPool::new();
let c = pool.symbol("c", Domain::Real);
let nc = pool.symbol_commutative("nc", Domain::Real, false);
let two = pool.integer(2_i32);
let mut nodes = vec![c, nc, two];
let pure = pool.add(vec![c, two]);
nodes.push(pure);
nodes.push(pool.pow(pure, two));
nodes.push(pool.func("sin", vec![pure]));
let tainted = pool.add(vec![nc, two]);
nodes.push(tainted);
nodes.push(pool.pow(tainted, two));
nodes.push(pool.func("sin", vec![tainted]));
nodes.push(pool.mul(vec![pure, tainted]));
nodes.push(pool.big_o(tainted));
nodes.push(pool.pred_lt(tainted, pure));
let mut deep = c;
for _ in 0..50 {
deep = pool.mul(vec![deep, two]);
nodes.push(deep);
}
let mut deep_nc = nc;
for _ in 0..50 {
deep_nc = pool.mul(vec![deep_nc, two]);
nodes.push(deep_nc);
}
for id in nodes {
assert_eq!(
mult_tree_is_commutative(&pool, id),
reference(&pool, id),
"cached flag disagrees with full walk for {}",
crate::kernel::display::render_unicode(id, &pool)
);
}
}
#[test]
fn noncommutative_blocks_canonical_sorting() {
let pool = ExprPool::new();
let a = pool.symbol_commutative("a", Domain::Real, false);
let b = pool.symbol_commutative("b", Domain::Real, false);
assert_ne!(pool.mul(vec![a, b]), pool.mul(vec![b, a]));
assert!(!mult_tree_is_commutative(&pool, pool.mul(vec![a, b])));
}
}