use std::marker::PhantomData;
use std::sync::Arc;
use parking_lot::RwLock;
use tracing::debug_span;
use crate::api::context::ContextInner;
use crate::base::errors::SymplexError;
use crate::base::node::{CtxId, ExprId};
pub use crate::simplify::simplify_engine::SimplifyOpts;
pub trait Sort: 'static + Clone + Send + Sync {}
#[derive(Clone)]
pub struct Numeric;
#[derive(Clone)]
pub struct Boolean;
#[derive(Clone)]
pub struct SetValued;
impl Sort for Numeric {}
impl Sort for Boolean {}
impl Sort for SetValued {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExprType {
Number,
Symbol,
Constant,
Add,
Mul,
Pow,
Neg,
Function,
Apply,
Derivative,
Integral,
Set,
Unevaluated,
}
#[derive(Clone)]
pub struct Expr<S: Sort> {
pub(crate) ctx_id: CtxId,
pub(crate) inner: Arc<RwLock<ContextInner>>,
id: ExprId,
pub(crate) _sort: PhantomData<S>,
}
pub type Ex = Expr<Numeric>;
impl AsRef<Ex> for Ex {
#[inline]
fn as_ref(&self) -> &Ex {
self
}
}
pub type BoolEx = Expr<Boolean>;
pub type SetEx = Expr<SetValued>;
impl<S: Sort> Expr<S> {
#[inline]
pub(crate) fn from_raw_parts(
ctx_id: CtxId,
inner: Arc<RwLock<ContextInner>>,
id: ExprId,
) -> Self {
Expr {
ctx_id,
inner,
id,
_sort: PhantomData,
}
}
#[inline]
pub(crate) fn wrap(&self, id: ExprId) -> Expr<S> {
Expr::from_raw_parts(self.ctx_id, Arc::clone(&self.inner), id)
}
#[inline]
pub(crate) fn wrap_as<T: Sort>(&self, id: ExprId) -> Expr<T> {
Expr::from_raw_parts(self.ctx_id, Arc::clone(&self.inner), id)
}
#[inline]
pub(crate) fn raw_id(&self) -> ExprId {
self.id
}
#[inline]
pub(crate) fn checked_id<T: Sort>(&self, other: &Expr<T>) -> ExprId {
if self.ctx_id != other.ctx_id {
panic!(
"symplex: cannot combine expressions from different contexts \
(context {} and context {}). All expressions in an operation \
must originate from the same Context.",
self.ctx_id.0, other.ctx_id.0
);
}
other.id
}
#[must_use]
pub fn context(&self) -> crate::api::context::Context {
crate::api::context::Context {
id: self.ctx_id,
inner: Arc::clone(&self.inner),
}
}
}
impl<S: Sort> Expr<S> {
#[inline]
pub fn id(&self) -> ExprId {
self.id
}
#[inline]
pub fn ctx_id(&self) -> CtxId {
self.ctx_id
}
#[must_use]
pub fn is_zero_structural(&self) -> bool {
self.inner.read().arena.is_zero_structural(self.id)
}
#[must_use]
pub fn is_one_structural(&self) -> bool {
self.inner.read().arena.is_one_structural(self.id)
}
#[must_use]
pub fn free_symbols(&self) -> Vec<Ex> {
let inner = self.inner.read();
let expr_ids = crate::base::walk::free_symbols(&inner.arena, self.id);
drop(inner);
expr_ids
.into_iter()
.map(|eid| self.wrap_as::<Numeric>(eid))
.collect()
}
#[must_use]
pub fn has_unevaluated(&self) -> bool {
let inner = self.inner.read();
crate::base::walk::has_unevaluated(&inner.arena, self.raw_id())
}
#[must_use]
pub fn count_ops(&self) -> usize {
let inner = self.inner.read();
inner.arena.count_ops(self.id)
}
#[must_use]
pub fn term_count(&self) -> usize {
let inner = self.inner.read();
match inner.arena.node(self.id) {
crate::base::node::ExprNode::Add(children) => children.len(),
_ => 1,
}
}
#[must_use]
pub fn args(&self) -> Vec<Expr<S>> {
let inner = self.inner.read();
let child_ids = inner.arena.node(self.id).children();
child_ids.iter().map(|&id| self.wrap(id)).collect()
}
#[must_use]
pub fn expr_type(&self) -> ExprType {
let inner = self.inner.read();
match inner.arena.node(self.id) {
crate::base::node::ExprNode::Num(_) => ExprType::Number,
crate::base::node::ExprNode::Symbol(_) => ExprType::Symbol,
crate::base::node::ExprNode::Pi
| crate::base::node::ExprNode::E
| crate::base::node::ExprNode::ImaginaryUnit
| crate::base::node::ExprNode::EulerGamma
| crate::base::node::ExprNode::Catalan
| crate::base::node::ExprNode::GoldenRatio
| crate::base::node::ExprNode::PhysicalConstant(_, _)
| crate::base::node::ExprNode::Infinity
| crate::base::node::ExprNode::NegInfinity
| crate::base::node::ExprNode::ComplexInfinity
| crate::base::node::ExprNode::NaN => ExprType::Constant,
crate::base::node::ExprNode::Add(_) => ExprType::Add,
crate::base::node::ExprNode::Mul(_) => ExprType::Mul,
crate::base::node::ExprNode::Pow(_, _) => ExprType::Pow,
crate::base::node::ExprNode::Neg(_) => ExprType::Neg,
crate::base::node::ExprNode::Sin(_)
| crate::base::node::ExprNode::Cos(_)
| crate::base::node::ExprNode::Tan(_)
| crate::base::node::ExprNode::Exp(_)
| crate::base::node::ExprNode::Ln(_)
| crate::base::node::ExprNode::Abs(_)
| crate::base::node::ExprNode::Asin(_)
| crate::base::node::ExprNode::Acos(_)
| crate::base::node::ExprNode::Atan(_)
| crate::base::node::ExprNode::Atan2(_, _)
| crate::base::node::ExprNode::Sinh(_)
| crate::base::node::ExprNode::Cosh(_)
| crate::base::node::ExprNode::Tanh(_)
| crate::base::node::ExprNode::Asinh(_)
| crate::base::node::ExprNode::Acosh(_)
| crate::base::node::ExprNode::Atanh(_)
| crate::base::node::ExprNode::Sign(_)
| crate::base::node::ExprNode::Floor(_)
| crate::base::node::ExprNode::Ceiling(_)
| crate::base::node::ExprNode::Min(_)
| crate::base::node::ExprNode::Max(_)
| crate::base::node::ExprNode::Sum(_, _, _, _)
| crate::base::node::ExprNode::Product_(_, _, _, _) => ExprType::Function,
crate::base::node::ExprNode::Apply(_, _) => ExprType::Apply,
crate::base::node::ExprNode::Derivative(_, _) => ExprType::Derivative,
crate::base::node::ExprNode::Integral(_, _) => ExprType::Integral,
crate::base::node::ExprNode::Factorial(_)
| crate::base::node::ExprNode::Binomial(_, _)
| crate::base::node::ExprNode::Gamma(_)
| crate::base::node::ExprNode::LogGamma(_)
| crate::base::node::ExprNode::Digamma(_)
| crate::base::node::ExprNode::Erf(_)
| crate::base::node::ExprNode::Erfc(_)
| crate::base::node::ExprNode::LambertW(_)
| crate::base::node::ExprNode::Beta(_, _)
| crate::base::node::ExprNode::Re(_)
| crate::base::node::ExprNode::Im(_)
| crate::base::node::ExprNode::Conjugate(_)
| crate::base::node::ExprNode::Arg(_)
| crate::base::node::ExprNode::Si(_)
| crate::base::node::ExprNode::Ci(_)
| crate::base::node::ExprNode::Ei(_)
| crate::base::node::ExprNode::Li(_)
| crate::base::node::ExprNode::Zeta(_)
| crate::base::node::ExprNode::Polygamma(_, _)
| crate::base::node::ExprNode::KroneckerDelta(_, _) => ExprType::Function,
crate::base::node::ExprNode::BoolTrue | crate::base::node::ExprNode::BoolFalse => {
ExprType::Constant
}
crate::base::node::ExprNode::Gt(_, _)
| crate::base::node::ExprNode::Ge(_, _)
| crate::base::node::ExprNode::Eq_(_, _)
| crate::base::node::ExprNode::Ne(_, _)
| crate::base::node::ExprNode::And(_)
| crate::base::node::ExprNode::Or(_)
| crate::base::node::ExprNode::Not(_)
| crate::base::node::ExprNode::Piecewise(_)
| crate::base::node::ExprNode::Heaviside(_)
| crate::base::node::ExprNode::DiracDelta(_) => ExprType::Function,
crate::base::node::ExprNode::EmptySet
| crate::base::node::ExprNode::UniversalSet
| crate::base::node::ExprNode::Interval(_, _, _)
| crate::base::node::ExprNode::FiniteSet(_)
| crate::base::node::ExprNode::SetUnion(_)
| crate::base::node::ExprNode::SetIntersection(_)
| crate::base::node::ExprNode::SetComplement(_, _) => ExprType::Set,
crate::base::node::ExprNode::RootOf(..) | crate::base::node::ExprNode::RootSum(..) => {
if crate::base::walk::free_symbols(&inner.arena, self.id).is_empty() {
ExprType::Constant
} else {
ExprType::Function
}
}
crate::base::node::ExprNode::DefiniteIntegral(..)
| crate::base::node::ExprNode::Limit(..)
| crate::base::node::ExprNode::Series(..)
| crate::base::node::ExprNode::LaplaceTransform(..)
| crate::base::node::ExprNode::InverseLaplaceTransform(..)
| crate::base::node::ExprNode::Residue(..)
| crate::base::node::ExprNode::DSolve(..)
| crate::base::node::ExprNode::ConditionSet(..) => ExprType::Unevaluated,
}
}
#[must_use = "returns a new expression with substitutions applied"]
pub fn subs(&self, old: &Ex, new: &Ex) -> Expr<S> {
let old_id = self.checked_id(old);
let new_id = self.checked_id(new);
let id = self
.inner
.write()
.arena
.subs_structural(self.id, old_id, new_id);
self.wrap(id)
}
#[must_use = "returns a new expression with substitutions applied"]
pub fn subs_i64(&self, old: &Ex, new: i64) -> Expr<S> {
let old_id = self.checked_id(old);
let mut inner = self.inner.write();
let new_id = inner.arena.int(new);
let id = inner.arena.subs_structural(self.id, old_id, new_id);
drop(inner);
self.wrap(id)
}
#[must_use = "returns a new expression with substitutions applied"]
pub fn subs_map(&self, replacements: &[(&Ex, &Ex)]) -> Expr<S> {
let pairs: smallvec::SmallVec<[(crate::base::node::ExprId, crate::base::node::ExprId); 4]> =
replacements
.iter()
.map(|(o, n)| (self.checked_id(o), self.checked_id(n)))
.collect();
let id = self
.inner
.write()
.arena
.subs_map_structural(self.id, &pairs);
self.wrap(id)
}
#[must_use = "returns the expanded form; does not modify in place"]
pub fn expand(&self) -> Expr<S> {
let _span = debug_span!("expand", expr = ?self.id).entered();
let id = self.inner.write().arena.expand_expr(self.id);
self.wrap(id)
}
#[must_use = "returns the simplified form; does not modify in place"]
pub fn simplify_with(&self, opts: &SimplifyOpts) -> Expr<S> {
let _span = debug_span!("simplify_with", expr = ?self.id).entered();
let result = {
let mut inner = self.inner.write();
crate::simplify::simplify_engine::unified_simplify(&mut inner.arena, self.id, opts)
};
self.wrap(result.expr)
}
#[must_use = "returns a serializable tree; does not modify in place"]
pub fn to_tree(&self) -> crate::output::tree::ExprTree {
let inner = self.inner.read();
crate::output::tree::expr_to_tree(&inner.arena, self.id)
}
pub fn to_json(&self) -> Result<String, SymplexError> {
serde_json::to_string(&self.to_tree()).map_err(|e| SymplexError::ComputationFailed {
operation: "to_json",
reason: e.to_string(),
})
}
pub fn to_json_pretty(&self) -> Result<String, SymplexError> {
serde_json::to_string_pretty(&self.to_tree()).map_err(|e| SymplexError::ComputationFailed {
operation: "to_json_pretty",
reason: e.to_string(),
})
}
#[must_use = "returns the stabilized expression and iteration count"]
pub fn apply_until_stable<F>(&self, max_iterations: usize, f: F) -> (Expr<S>, usize)
where
F: Fn(&Expr<S>) -> Expr<S>,
{
let mut current = self.clone();
for i in 0..max_iterations {
let next = f(¤t);
if next.raw_id() == current.raw_id() && next.ctx_id == current.ctx_id {
return (current, i);
}
current = next;
}
(current, max_iterations)
}
}
impl Expr<Numeric> {
#[must_use]
pub fn contains(&self, needle: &Ex) -> bool {
let needle_id = self.checked_id(needle);
let inner = self.inner.read();
crate::base::walk::contains(&inner.arena, self.id, needle_id)
}
#[must_use = "returns the evaluated form; does not modify in place"]
pub fn eval(&self) -> Ex {
let _span = debug_span!("eval", expr = ?self.id).entered();
let id = self.inner.write().arena.eval_expr(self.id);
self.wrap(id)
}
#[must_use = "returns the simplified form; does not modify in place"]
pub fn simplify(&self) -> Ex {
let _span = debug_span!("simplify", expr = ?self.id).entered();
let result = {
let mut inner = self.inner.write();
crate::simplify::simplify_engine::unified_simplify(
&mut inner.arena,
self.id,
&crate::simplify::simplify_engine::SimplifyOpts::default(),
)
};
self.wrap(result.expr)
}
}
impl Expr<Boolean> {
#[must_use]
pub fn contains(&self, needle: &Ex) -> bool {
let needle_id = self.checked_id(needle);
let inner = self.inner.read();
crate::base::walk::contains(&inner.arena, self.id, needle_id)
}
#[must_use = "returns a new expression; does not modify in place"]
pub fn and(&self, other: &BoolEx) -> BoolEx {
let other_id = self.checked_id(other);
let id = self.inner.write().arena.and(&[self.id, other_id]);
self.wrap(id)
}
#[must_use = "returns a new expression; does not modify in place"]
pub fn or(&self, other: &BoolEx) -> BoolEx {
let other_id = self.checked_id(other);
let id = self.inner.write().arena.or(&[self.id, other_id]);
self.wrap(id)
}
#[must_use = "returns a new expression; does not modify in place"]
pub fn not(&self) -> BoolEx {
let id = self.inner.write().arena.not(self.id);
self.wrap(id)
}
#[must_use]
pub fn xor(&self, other: &BoolEx) -> BoolEx {
self.and(&other.not()).or(&self.not().and(other))
}
#[must_use]
pub fn implies(&self, other: &BoolEx) -> BoolEx {
self.not().or(other)
}
#[must_use]
pub fn equivalent(&self, other: &BoolEx) -> BoolEx {
self.implies(other).and(&other.implies(self))
}
#[must_use]
pub fn nand(&self, other: &BoolEx) -> BoolEx {
self.and(other).not()
}
#[must_use]
pub fn nor(&self, other: &BoolEx) -> BoolEx {
self.or(other).not()
}
#[must_use]
pub fn ite(&self, then_: &BoolEx, else_: &BoolEx) -> BoolEx {
let _ = self.checked_id(then_);
let _ = self.checked_id(else_);
self.and(then_).or(&self.not().and(else_))
}
pub fn into_ex(self) -> Ex {
Ex {
ctx_id: self.ctx_id,
inner: self.inner,
id: self.id,
_sort: PhantomData,
}
}
pub fn as_ex(&self) -> Ex {
Ex {
ctx_id: self.ctx_id,
inner: Arc::clone(&self.inner),
id: self.id,
_sort: PhantomData,
}
}
}
impl Expr<SetValued> {
#[must_use = "returns a new expression; does not modify in place"]
pub fn union(&self, other: &SetEx) -> SetEx {
let other_id = self.checked_id(other);
let id = self.inner.write().arena.set_union(&[self.id, other_id]);
self.wrap(id)
}
#[must_use = "returns a new expression; does not modify in place"]
pub fn intersection(&self, other: &SetEx) -> SetEx {
let id = self
.inner
.write()
.arena
.set_intersection(&[self.id, self.checked_id(other)]);
self.wrap(id)
}
#[must_use = "returns a new expression; does not modify in place"]
pub fn complement(&self, other: &SetEx) -> SetEx {
let other_id = self.checked_id(other);
let id = self.inner.write().arena.set_complement(self.id, other_id);
self.wrap(id)
}
pub fn into_ex(self) -> Ex {
Ex {
ctx_id: self.ctx_id,
inner: self.inner,
id: self.id,
_sort: PhantomData,
}
}
pub fn as_ex(&self) -> Ex {
Ex {
ctx_id: self.ctx_id,
inner: Arc::clone(&self.inner),
id: self.id,
_sort: PhantomData,
}
}
}