use crate::kernel::expr::ExprData;
use crate::kernel::pool::{ExprPool, POS_INFINITY_SYMBOL};
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.is_mult_commutative(expr)
}
pub fn is_non_finite_atom(pool: &ExprPool, expr: ExprId) -> bool {
pool.with(expr, |d| match d {
ExprData::Symbol { name, .. } => name == POS_INFINITY_SYMBOL,
ExprData::Float(f) => !f.inner.is_finite(),
_ => false,
})
}
pub fn contains_non_finite_atom(pool: &ExprPool, expr: ExprId) -> bool {
pool.has_non_finite(expr)
}
#[cfg(test)]
fn contains_noncommutative_walk(pool: &ExprPool, expr: ExprId) -> bool {
let rec = |c: ExprId| contains_noncommutative_walk(pool, c);
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| rec(c)),
ExprData::Pow { base, exp } => rec(*base) || rec(*exp),
ExprData::Func { args, .. } => args.iter().any(|&c| rec(c)),
ExprData::Piecewise { branches, default } => {
branches.iter().any(|(c, v)| rec(*c) || rec(*v)) || rec(*default)
}
ExprData::Predicate { args, .. } => args.iter().any(|&c| rec(c)),
ExprData::Forall { var, body } | ExprData::Exists { var, body } => rec(*var) || rec(*body),
ExprData::BigO(inner) => rec(*inner),
ExprData::RootSum { poly, body, .. } => rec(*poly) || rec(*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 contains_noncommutative_matches_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 half = pool.rational(1, 2);
let f = pool.float(0.5, 53);
let mut nodes = vec![c, nc, two, half, f];
for leaf in [c, nc] {
let s = pool.add(vec![leaf, two]);
nodes.push(s);
nodes.push(pool.mul(vec![s, half]));
nodes.push(pool.pow(two, s));
nodes.push(pool.func("atan2", vec![c, s]));
nodes.push(pool.pred_lt(s, f));
nodes.push(pool.piecewise(vec![(pool.pred_gt(c, two), s)], two));
nodes.push(pool.piecewise(vec![(pool.pred_gt(s, two), c)], two));
nodes.push(pool.piecewise(vec![(pool.pred_gt(c, two), c)], s));
nodes.push(pool.forall(c, pool.pred_ge(s, two)));
nodes.push(pool.exists(c, pool.pred_ge(s, two)));
nodes.push(pool.big_o(s));
nodes.push(pool.root_sum(s, c, c));
nodes.push(pool.root_sum(c, c, s));
nodes.push(pool.root_sum(c, leaf, c));
}
for id in nodes {
assert_eq!(
expr_contains_noncommutative_symbol(&pool, id),
contains_noncommutative_walk(&pool, id),
"cached flag disagrees with 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])));
}
}