diff --git a/examples/coefficient_ring.rs b/examples/coefficient_ring.rs index 146598e6..77feb0ec 100644 --- a/examples/coefficient_ring.rs +++ b/examples/coefficient_ring.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use symbolica::{atom::Atom, state::State}; +use symbolica::{ + atom::{Atom, AtomCore}, + state::State, +}; fn main() { let expr = Atom::parse("x*z+x*(y+2)^-1*(y+z+1)").unwrap(); diff --git a/examples/collect.rs b/examples/collect.rs index 01d7133c..91a75cc3 100644 --- a/examples/collect.rs +++ b/examples/collect.rs @@ -1,4 +1,8 @@ -use symbolica::{atom::Atom, fun, state::State}; +use symbolica::{ + atom::{Atom, AtomCore}, + fun, + state::State, +}; fn main() { let input = Atom::parse("x*(1+a)+x*5*y+f(5,x)+2+y^2+x^2 + x^3").unwrap(); diff --git a/examples/derivative.rs b/examples/derivative.rs index 1a41f2a2..91afb0b1 100644 --- a/examples/derivative.rs +++ b/examples/derivative.rs @@ -1,4 +1,7 @@ -use symbolica::{atom::Atom, state::State}; +use symbolica::{ + atom::{Atom, AtomCore}, + state::State, +}; fn main() { let x = State::get_symbol("x"); diff --git a/examples/evaluate.rs b/examples/evaluate.rs index 280c3b3f..06a1977a 100644 --- a/examples/evaluate.rs +++ b/examples/evaluate.rs @@ -1,4 +1,5 @@ use ahash::HashMap; +use symbolica::atom::AtomCore; use symbolica::evaluate::EvaluationFn; use symbolica::{atom::Atom, state::State}; diff --git a/examples/expansion.rs b/examples/expansion.rs index 45f2f543..fac3fd6b 100644 --- a/examples/expansion.rs +++ b/examples/expansion.rs @@ -1,4 +1,4 @@ -use symbolica::atom::Atom; +use symbolica::atom::{Atom, AtomCore}; fn main() { let input = Atom::parse("(1+x)^3").unwrap(); diff --git a/examples/factorization.rs b/examples/factorization.rs index 19641ccb..a795a62e 100644 --- a/examples/factorization.rs +++ b/examples/factorization.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{finite_field::Zp, integer::Z}, poly::{factor::Factorize, polynomial::MultivariatePolynomial, Variable}, state::State, diff --git a/examples/fibonacci.rs b/examples/fibonacci.rs index 41df7b64..83a33ddd 100644 --- a/examples/fibonacci.rs +++ b/examples/fibonacci.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::{Atom, AtomView}, + atom::{Atom, AtomCore, AtomView}, id::{Match, Pattern, WildcardRestriction}, state::{RecycledAtom, State}, }; @@ -35,7 +35,7 @@ fn main() { target.replace_all_into(&pattern, &rhs, Some(&restrictions), None, &mut out); let mut out2 = RecycledAtom::new(); - out.expand_into(&mut out2); + out.expand_into(None, &mut out2); out2.replace_all_into(&lhs_zero_pat, &rhs_one, None, None, &mut out); diff --git a/examples/groebner_basis.rs b/examples/groebner_basis.rs index cd87f8a3..524b46d5 100644 --- a/examples/groebner_basis.rs +++ b/examples/groebner_basis.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::finite_field::Zp, poly::{groebner::GroebnerBasis, polynomial::MultivariatePolynomial, GrevLexOrder}, state::State, diff --git a/examples/nested_evaluation.rs b/examples/nested_evaluation.rs index bbf58154..e2638859 100644 --- a/examples/nested_evaluation.rs +++ b/examples/nested_evaluation.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::rational::Rational, evaluate::{CompileOptions, FunctionMap, InlineASM, OptimizationSettings}, state::State, diff --git a/examples/optimize.rs b/examples/optimize.rs index de4996b4..14335946 100644 --- a/examples/optimize.rs +++ b/examples/optimize.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::rational::Q, poly::evaluate::{BorrowedHornerScheme, InstructionSetPrinter}, }; diff --git a/examples/optimize_multiple.rs b/examples/optimize_multiple.rs index 478fc98a..06d6c0b1 100644 --- a/examples/optimize_multiple.rs +++ b/examples/optimize_multiple.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::rational::Q, poly::evaluate::{HornerScheme, InstructionSetPrinter}, }; diff --git a/examples/partition.rs b/examples/partition.rs index 9a33d6de..eb1969f2 100644 --- a/examples/partition.rs +++ b/examples/partition.rs @@ -1,4 +1,9 @@ -use symbolica::{atom::Atom, id::Pattern, state::State, transformer::Transformer}; +use symbolica::{ + atom::{Atom, AtomCore}, + id::Pattern, + state::State, + transformer::Transformer, +}; fn main() { let input = Atom::parse("f(1,3,2,3,1)").unwrap(); diff --git a/examples/pattern_match.rs b/examples/pattern_match.rs index bfeefb67..88665e89 100644 --- a/examples/pattern_match.rs +++ b/examples/pattern_match.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, id::{Condition, Match, MatchSettings}, state::State, }; diff --git a/examples/pattern_restrictions.rs b/examples/pattern_restrictions.rs index cc1e1afb..68dfebd7 100644 --- a/examples/pattern_restrictions.rs +++ b/examples/pattern_restrictions.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::{Atom, AtomView}, + atom::{Atom, AtomCore, AtomView}, coefficient::CoefficientView, domains::finite_field, id::{Condition, Match, MatchSettings, WildcardRestriction}, @@ -9,7 +9,7 @@ fn main() { let expr = Atom::parse("f(1,2,3,4,5,6,7)").unwrap(); let pat_expr = Atom::parse("f(x__,y__,z__,w__)").unwrap(); - let pattern = pat_expr.as_view().into_pattern(); + let pattern = pat_expr.as_view().to_pattern(); let x = State::get_symbol("x__"); let y = State::get_symbol("y__"); diff --git a/examples/polynomial_gcd.rs b/examples/polynomial_gcd.rs index 8604d091..c679a9a4 100644 --- a/examples/polynomial_gcd.rs +++ b/examples/polynomial_gcd.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{integer::Z, rational::Q}, }; use tracing_subscriber::{fmt, prelude::*, util::SubscriberInitExt, EnvFilter}; diff --git a/examples/rational_polynomial.rs b/examples/rational_polynomial.rs index 5cad80c4..f7de5a21 100644 --- a/examples/rational_polynomial.rs +++ b/examples/rational_polynomial.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{integer::Z, rational_polynomial::RationalPolynomial}, }; diff --git a/examples/replace_all.rs b/examples/replace_all.rs index 539f9565..b852afb7 100644 --- a/examples/replace_all.rs +++ b/examples/replace_all.rs @@ -1,4 +1,7 @@ -use symbolica::{atom::Atom, id::Pattern}; +use symbolica::{ + atom::{Atom, AtomCore}, + id::Pattern, +}; fn main() { let expr = Atom::parse(" f(1,2,x) + f(1,2,3)").unwrap(); diff --git a/examples/replace_once.rs b/examples/replace_once.rs index 5d4d1ca5..b390aa12 100644 --- a/examples/replace_once.rs +++ b/examples/replace_once.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, id::{Condition, MatchSettings}, }; @@ -8,9 +8,9 @@ fn main() { let pat_expr = Atom::parse("f(x_)").unwrap(); let rhs_expr = Atom::parse("g(x_)").unwrap(); - let rhs = rhs_expr.as_view().into_pattern().into(); + let rhs = rhs_expr.as_view().to_pattern().into(); - let pattern = pat_expr.as_view().into_pattern(); + let pattern = pat_expr.as_view().to_pattern(); let restrictions = Condition::default(); let settings = MatchSettings::default(); diff --git a/examples/series.rs b/examples/series.rs index 9e4dca4e..ae128b48 100644 --- a/examples/series.rs +++ b/examples/series.rs @@ -1,4 +1,7 @@ -use symbolica::{atom::Atom, state::State}; +use symbolica::{ + atom::{Atom, AtomCore}, + state::State, +}; fn main() { let x = State::get_symbol("x"); diff --git a/examples/solve_linear_system.rs b/examples/solve_linear_system.rs index b08b5ae9..4512ded6 100644 --- a/examples/solve_linear_system.rs +++ b/examples/solve_linear_system.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use symbolica::{ - atom::{representation::InlineVar, Atom, AtomView}, + atom::{representation::InlineVar, Atom, AtomCore, AtomView}, domains::{ integer::Z, rational::Q, diff --git a/examples/streaming.rs b/examples/streaming.rs index f7f98dc5..4d5360c2 100644 --- a/examples/streaming.rs +++ b/examples/streaming.rs @@ -1,6 +1,6 @@ use brotli::CompressorWriter; use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, id::Pattern, streaming::{TermStreamer, TermStreamerConfig}, }; diff --git a/examples/tree_replace.rs b/examples/tree_replace.rs index 55bd10f8..50408f7e 100644 --- a/examples/tree_replace.rs +++ b/examples/tree_replace.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::Atom, + atom::{Atom, AtomCore}, id::{Condition, Match, MatchSettings, PatternAtomTreeIterator}, state::State, }; @@ -8,7 +8,7 @@ fn main() { let expr = Atom::parse("f(z)*f(f(x))*f(y)").unwrap(); let pat_expr = Atom::parse("f(x_)").unwrap(); - let pattern = pat_expr.as_view().into_pattern(); + let pattern = pat_expr.to_pattern(); let restrictions = Condition::default(); let settings = MatchSettings::default(); diff --git a/src/api/python.rs b/src/api/python.rs index bc711d97..f9e03964 100644 --- a/src/api/python.rs +++ b/src/api/python.rs @@ -32,7 +32,7 @@ use smartstring::{LazyCompact, SmartString}; use pyo3::pymodule; use crate::{ - atom::{Atom, AtomType, AtomView, ListIterator, Symbol}, + atom::{Atom, AtomCore, AtomType, AtomView, ListIterator, Symbol}, coefficient::CoefficientView, domains::{ algebraic_number::AlgebraicExtension, diff --git a/src/atom.rs b/src/atom.rs index 76e26e53..433efd2b 100644 --- a/src/atom.rs +++ b/src/atom.rs @@ -1,7 +1,8 @@ mod coefficient; +mod core; pub mod representation; -use representation::{InlineNum, InlineVar}; +use representation::InlineVar; use crate::{ coefficient::Coefficient, @@ -12,6 +13,7 @@ use crate::{ }; use std::{cmp::Ordering, hash::Hash, ops::DerefMut, str::FromStr}; +pub use self::core::AtomCore; pub use self::representation::{ Add, AddView, Fun, ListIterator, ListSlice, Mul, MulView, Num, NumView, Pow, PowView, Var, VarView, @@ -20,7 +22,7 @@ use self::representation::{FunView, RawAtom}; /// A symbol, for example the name of a variable or the name of a function, /// together with its properties. -/// Should be created using `get_symbol` of `State`. +/// Should be created using [State::get_symbol]. #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Symbol { id: u32, @@ -312,49 +314,6 @@ impl<'a> AtomOrView<'a> { } } -/// A trait for any type that can be converted into an `AtomView`. -/// To be used for functions that accept any argument that can be -/// converted to an `AtomView`. -pub trait AsAtomView { - fn as_atom_view(&self) -> AtomView; -} - -impl<'a> AsAtomView for AtomView<'a> { - fn as_atom_view(&self) -> AtomView<'a> { - *self - } -} - -impl AsAtomView for InlineVar { - fn as_atom_view(&self) -> AtomView { - self.as_view() - } -} - -impl AsAtomView for InlineNum { - fn as_atom_view(&self) -> AtomView { - self.as_view() - } -} - -impl> AsAtomView for T { - fn as_atom_view(&self) -> AtomView { - self.as_ref().as_view() - } -} - -impl<'a> AsAtomView for AtomOrView<'a> { - fn as_atom_view(&self) -> AtomView { - self.as_view() - } -} - -impl AsRef for Atom { - fn as_ref(&self) -> &Atom { - self - } -} - impl<'a> AtomView<'a> { pub fn to_owned(&self) -> Atom { let mut a = Atom::default(); @@ -852,12 +811,12 @@ impl Atom { } } -/// A constructor of a function. Consider using the [`fun!`] macro instead. +/// A constructor of a function. Consider using the [crate::fun!] macro instead. /// /// For example: /// ``` /// # use symbolica::{ -/// # atom::{Atom, AsAtomView, FunctionBuilder}, +/// # atom::{Atom, AtomCore, FunctionBuilder}, /// # state::{FunctionAttribute, State}, /// # }; /// # fn main() { @@ -885,7 +844,7 @@ impl FunctionBuilder { } /// Add an argument to the function. - pub fn add_arg(mut self, arg: T) -> FunctionBuilder { + pub fn add_arg(mut self, arg: T) -> FunctionBuilder { if let Atom::Fun(f) = self.handle.deref_mut() { f.add_arg(arg.as_atom_view()); } @@ -894,7 +853,7 @@ impl FunctionBuilder { } /// Add multiple arguments to the function. - pub fn add_args(mut self, args: &[T]) -> FunctionBuilder { + pub fn add_args(mut self, args: &[T]) -> FunctionBuilder { if let Atom::Fun(f) = self.handle.deref_mut() { for a in args { f.add_arg(a.as_atom_view()); @@ -1023,7 +982,7 @@ impl Atom { } /// Take the `self` to the power `exp`. Use [`Atom::npow()`] for a numerical power and [`Atom::rpow()`] for the reverse operation. - pub fn pow(&self, exp: T) -> Atom { + pub fn pow(&self, exp: T) -> Atom { Workspace::get_local().with(|ws| { let mut t = ws.new_atom(); self.as_view() @@ -1035,7 +994,7 @@ impl Atom { } /// Take `base` to the power `self`. - pub fn rpow(&self, base: T) -> Atom { + pub fn rpow(&self, base: T) -> Atom { Workspace::get_local().with(|ws| { let mut t = ws.new_atom(); base.as_atom_view() @@ -1047,7 +1006,7 @@ impl Atom { } /// Add the atoms in `args`. - pub fn add_many<'a, T: AsAtomView + Copy>(args: &[T]) -> Atom { + pub fn add_many<'a, T: AtomCore + Copy>(args: &[T]) -> Atom { let mut out = Atom::new(); Workspace::get_local().with(|ws| { let mut t = ws.new_atom(); @@ -1062,7 +1021,7 @@ impl Atom { } /// Multiply the atoms in `args`. - pub fn mul_many<'a, T: AsAtomView + Copy>(args: &[T]) -> Atom { + pub fn mul_many<'a, T: AtomCore + Copy>(args: &[T]) -> Atom { let mut out = Atom::new(); Workspace::get_local().with(|ws| { let mut t = ws.new_atom(); @@ -1571,9 +1530,19 @@ impl> std::ops::Div for Atom { } } +impl AsRef for Atom { + fn as_ref(&self) -> &Atom { + self + } +} + #[cfg(test)] mod test { - use crate::{atom::Atom, fun, state::State}; + use crate::{ + atom::{Atom, AtomCore}, + fun, + state::State, + }; #[test] fn debug() { diff --git a/src/atom/core.rs b/src/atom/core.rs new file mode 100644 index 00000000..759b8478 --- /dev/null +++ b/src/atom/core.rs @@ -0,0 +1,628 @@ +use ahash::{HashMap, HashSet}; +use rayon::ThreadPool; + +use crate::{ + coefficient::{Coefficient, CoefficientView, ConvertToRing}, + domains::{ + atom::AtomField, + factorized_rational_polynomial::{ + FactorizedRationalPolynomial, FromNumeratorAndFactorizedDenominator, + }, + float::{Real, SingleFloat}, + integer::Z, + rational::Rational, + rational_polynomial::{ + FromNumeratorAndDenominator, RationalPolynomial, RationalPolynomialField, + }, + EuclideanDomain, InternalOrdering, + }, + evaluate::{EvalTree, EvaluationFn, ExpressionEvaluator, FunctionMap, OptimizationSettings}, + id::{ + BorrowPatternOrMap, BorrowReplacement, Condition, ConditionResult, Context, MatchSettings, + Pattern, PatternAtomTreeIterator, PatternOrMap, PatternRestriction, ReplaceIterator, + }, + poly::{ + factor::Factorize, gcd::PolynomialGCD, polynomial::MultivariatePolynomial, series::Series, + Exponent, PositiveExponent, Variable, + }, + printer::{AtomPrinter, PrintOptions, PrintState}, + state::Workspace, + tensors::matrix::Matrix, +}; +use std::sync::Arc; + +use super::{ + representation::{InlineNum, InlineVar}, + Atom, AtomOrView, AtomView, Symbol, +}; + +/// All core features of expressions, such as expansion and +/// pattern matching that leave the expression unchanged. +pub trait AtomCore { + /// Take a view of the atom. + fn as_atom_view(&self) -> AtomView; + + /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g. + /// + /// ```math + /// collect(x + x * y + x^2, x) = x * (1+y) + x^2 + /// ``` + /// + /// Both the *key* (the quantity collected in) and its coefficient can be mapped using + /// `key_map` and `coeff_map` respectively. + fn collect( + &self, + x: T, + key_map: Option>, + coeff_map: Option>, + ) -> Atom { + self.as_atom_view().collect::(x, key_map, coeff_map) + } + + /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g. + /// + /// ```math + /// collect(x + x * y + x^2, x) = x * (1+y) + x^2 + /// ``` + /// + /// Both the *key* (the quantity collected in) and its coefficient can be mapped using + /// `key_map` and `coeff_map` respectively. + fn collect_multiple( + &self, + xs: &[T], + key_map: Option>, + coeff_map: Option>, + ) -> Atom { + self.as_atom_view() + .collect_multiple::(xs, key_map, coeff_map) + } + + /// Collect terms involving the same power of `x` in `xs`, where `xs` is a list of indeterminates. + /// Return the list of key-coefficient pairs + fn coefficient_list(&self, xs: &[T]) -> Vec<(Atom, Atom)> { + self.as_atom_view().coefficient_list::(xs) + } + + /// Collect terms involving the literal occurrence of `x`. + fn coefficient(&self, x: T) -> Atom { + Workspace::get_local().with(|ws| { + self.as_atom_view() + .coefficient_with_ws(x.as_atom_view(), ws) + }) + } + + /// Write the expression over a common denominator. + fn together(&self) -> Atom { + self.as_atom_view().together() + } + + /// Write the expression as a sum of terms with minimal denominators. + fn apart(&self, x: Symbol) -> Atom { + self.as_atom_view().apart(x) + } + + /// Cancel all common factors between numerators and denominators. + /// Any non-canceling parts of the expression will not be rewritten. + fn cancel(&self) -> Atom { + self.as_atom_view().cancel() + } + + /// Factor the expression over the rationals. + fn factor(&self) -> Atom { + self.as_atom_view().factor() + } + + /// Collect numerical factors by removing the numerical content from additions. + /// For example, `-2*x + 4*x^2 + 6*x^3` will be transformed into `-2*(x - 2*x^2 - 3*x^3)`. + /// + /// The first argument of the addition is normalized to a positive quantity. + fn collect_num(&self) -> Atom { + self.as_atom_view().collect_num() + } + + /// Expand an expression. The function [AtomCore::expand_via_poly] may be faster. + fn expand(&self) -> Atom { + self.as_atom_view().expand() + } + + /// Expand the expression by converting it to a polynomial, optionally + /// only in the indeterminate `var`. The parameter `E` should be a numerical type + /// that fits the largest exponent in the expanded expression. Often, + /// `u8` or `u16` is sufficient. + fn expand_via_poly(&self, var: Option) -> Atom { + self.as_atom_view() + .expand_via_poly::(var.as_ref().map(|x| x.as_atom_view())) + } + + /// Expand an expression in the variable `var`. The function [AtomCore::expand_via_poly] may be faster. + fn expand_in(&self, var: T) -> Atom { + self.as_atom_view().expand_in(var.as_atom_view()) + } + + /// Expand an expression in the variable `var`. + fn expand_in_symbol(&self, var: Symbol) -> Atom { + self.as_atom_view() + .expand_in(InlineVar::from(var).as_view()) + } + + /// Expand an expression, returning `true` iff the expression changed. + fn expand_into(&self, var: Option, out: &mut Atom) -> bool { + self.as_atom_view().expand_into(var, out) + } + + /// Distribute numbers in the expression, for example: + /// `2*(x+y)` -> `2*x+2*y`. + fn expand_num(&self) -> Atom { + self.as_atom_view().expand_num() + } + + /// Take a derivative of the expression with respect to `x`. + fn derivative(&self, x: Symbol) -> Atom { + self.as_atom_view().derivative(x) + } + + /// Take a derivative of the expression with respect to `x` and + /// write the result in `out`. + /// Returns `true` if the derivative is non-zero. + fn derivative_into(&self, x: Symbol, out: &mut Atom) -> bool { + self.as_atom_view().derivative_into(x, out) + } + + /// Series expand in `x` around `expansion_point` to depth `depth`. + fn series( + &self, + x: Symbol, + expansion_point: T, + depth: Rational, + depth_is_absolute: bool, + ) -> Result, &'static str> { + self.as_atom_view() + .series(x, expansion_point.as_atom_view(), depth, depth_is_absolute) + } + + /// Find the root of a function in `x` numerically over the reals using Newton's method. + fn nsolve( + &self, + x: Symbol, + init: N, + prec: N, + max_iterations: usize, + ) -> Result { + self.as_atom_view().nsolve(x, init, prec, max_iterations) + } + + /// Solve a non-linear system numerically over the reals using Newton's method. + fn nsolve_system< + N: SingleFloat + Real + PartialOrd + InternalOrdering + Eq + std::hash::Hash, + T: AtomCore, + >( + system: &[T], + vars: &[Symbol], + init: &[N], + prec: N, + max_iterations: usize, + ) -> Result, String> { + AtomView::nsolve_system(system, vars, init, prec, max_iterations) + } + + /// Solve a system that is linear in `vars`, if possible. + /// Each expression in `system` is understood to yield 0. + fn solve_linear_system( + system: &[T1], + vars: &[T2], + ) -> Result, String> { + AtomView::solve_linear_system::(system, vars) + } + + /// Convert a system of linear equations to a matrix representation, returning the matrix + /// and the right-hand side. + fn system_to_matrix( + system: &[T1], + vars: &[T2], + ) -> Result< + ( + Matrix>, + Matrix>, + ), + String, + > { + AtomView::system_to_matrix::(system, vars) + } + + /// Evaluate a (nested) expression a single time. + /// For repeated evaluations, use [Self::evaluator()] and convert + /// to an optimized version or generate a compiled version of your expression. + /// + /// All variables and all user functions in the expression must occur in the map. + fn evaluate<'b, T: Real, F: Fn(&Rational) -> T + Copy>( + &'b self, + coeff_map: F, + const_map: &HashMap, T>, + function_map: &HashMap>, + cache: &mut HashMap, T>, + ) -> Result { + self.as_atom_view() + .evaluate(coeff_map, const_map, function_map, cache) + } + + /// Convert nested expressions to a tree suitable for repeated evaluations with + /// different values for `params`. + /// All variables and all user functions in the expression must occur in the map. + fn to_evaluation_tree<'a>( + &'a self, + fn_map: &FunctionMap<'a, Rational>, + params: &[Atom], + ) -> Result, String> { + self.as_atom_view().to_evaluation_tree(fn_map, params) + } + + /// Create an efficient evaluator for a (nested) expression. + /// All free parameters must appear in `params` and all other variables + /// and user functions in the expression must occur in the function map. + /// The function map may have nested expressions. + fn evaluator<'a>( + &'a self, + fn_map: &FunctionMap<'a, Rational>, + params: &[Atom], + optimization_settings: OptimizationSettings, + ) -> Result, String> { + let mut tree = self.to_evaluation_tree(fn_map, params)?; + Ok(tree.optimize( + optimization_settings.horner_iterations, + optimization_settings.n_cores, + optimization_settings.hot_start.clone(), + optimization_settings.verbose, + )) + } + + /// Convert nested expressions to a tree suitable for repeated evaluations with + /// different values for `params`. + /// All variables and all user functions in the expression must occur in the map. + fn evaluator_multiple<'a>( + exprs: &[AtomView<'a>], + fn_map: &FunctionMap<'a, Rational>, + params: &[Atom], + optimization_settings: OptimizationSettings, + ) -> Result, String> { + let mut tree = AtomView::to_eval_tree_multiple(exprs, fn_map, params)?; + Ok(tree.optimize( + optimization_settings.horner_iterations, + optimization_settings.n_cores, + optimization_settings.hot_start.clone(), + optimization_settings.verbose, + )) + } + + /// Check if the expression could be 0, using (potentially) numerical sampling with + /// a given tolerance and number of iterations. + fn zero_test(&self, iterations: usize, tolerance: f64) -> ConditionResult { + self.as_atom_view().zero_test(iterations, tolerance) + } + + /// Set the coefficient ring to the multivariate rational polynomial with `vars` variables. + fn set_coefficient_ring(&self, vars: &Arc>) -> Atom { + self.as_atom_view().set_coefficient_ring(vars) + } + + /// Convert all coefficients to floats with a given precision `decimal_prec``. + /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. + fn coefficients_to_float(&self, decimal_prec: u32) -> Atom { + let mut a = Atom::new(); + self.as_atom_view() + .coefficients_to_float_into(decimal_prec, &mut a); + a + } + + /// Convert all coefficients to floats with a given precision `decimal_prec``. + /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. + fn coefficients_to_float_into(&self, decimal_prec: u32, out: &mut Atom) { + self.as_atom_view() + .coefficients_to_float_into(decimal_prec, out); + } + + /// Map all coefficients using a given function. + fn map_coefficient Coefficient + Copy>(&self, f: F) -> Atom { + self.as_atom_view().map_coefficient(f) + } + + /// Map all coefficients using a given function. + fn map_coefficient_into Coefficient + Copy>( + &self, + f: F, + out: &mut Atom, + ) { + self.as_atom_view().map_coefficient_into(f, out); + } + + /// Map all floating point and rational coefficients to the best rational approximation + /// in the interval `[self*(1-relative_error),self*(1+relative_error)]`. + fn rationalize_coefficients(&self, relative_error: &Rational) -> Atom { + self.as_atom_view().rationalize_coefficients(relative_error) + } + + /// Convert the atom to a polynomial, optionally in the variable ordering + /// specified by `var_map`. If new variables are encountered, they are + /// added to the variable map. Similarly, non-polynomial parts are automatically + /// defined as a new independent variable in the polynomial. + fn to_polynomial( + &self, + field: &R, + var_map: Option>>, + ) -> MultivariatePolynomial { + self.as_atom_view().to_polynomial(field, var_map) + } + + /// Convert the atom to a polynomial in specific variables. + /// All other parts will be collected into the coefficient, which + /// is a general expression. + /// + /// This routine does not perform expansions. + fn to_polynomial_in_vars( + &self, + var_map: &Arc>, + ) -> MultivariatePolynomial { + self.as_atom_view().to_polynomial_in_vars(var_map) + } + + /// Convert the atom to a rational polynomial, optionally in the variable ordering + /// specified by `var_map`. If new variables are encountered, they are + /// added to the variable map. Similarly, non-rational polynomial parts are automatically + /// defined as a new independent variable in the rational polynomial. + fn to_rational_polynomial< + R: EuclideanDomain + ConvertToRing, + RO: EuclideanDomain + PolynomialGCD, + E: PositiveExponent, + >( + &self, + field: &R, + out_field: &RO, + var_map: Option>>, + ) -> RationalPolynomial + where + RationalPolynomial: + FromNumeratorAndDenominator + FromNumeratorAndDenominator, + { + self.as_atom_view() + .to_rational_polynomial(field, out_field, var_map) + } + + /// Convert the atom to a rational polynomial with factorized denominators, optionally in the variable ordering + /// specified by `var_map`. If new variables are encountered, they are + /// added to the variable map. Similarly, non-rational polynomial parts are automatically + /// defined as a new independent variable in the rational polynomial. + fn to_factorized_rational_polynomial< + R: EuclideanDomain + ConvertToRing, + RO: EuclideanDomain + PolynomialGCD, + E: PositiveExponent, + >( + &self, + field: &R, + out_field: &RO, + var_map: Option>>, + ) -> FactorizedRationalPolynomial + where + FactorizedRationalPolynomial: FromNumeratorAndFactorizedDenominator + + FromNumeratorAndFactorizedDenominator, + MultivariatePolynomial: Factorize, + { + self.as_atom_view() + .to_factorized_rational_polynomial(field, out_field, var_map) + } + + // Format the atom. + fn format( + &self, + fmt: &mut W, + opts: &PrintOptions, + print_state: PrintState, + ) -> Result { + self.as_atom_view().format(fmt, opts, print_state) + } + + /// Construct a printer for the atom with special options. + fn printer<'a>(&'a self, opts: PrintOptions) -> AtomPrinter<'a> { + AtomPrinter::new_with_options(self.as_atom_view(), opts) + } + + /// Print the atom in a form that is unique and independent of any implementation details. + /// + /// Anti-symmetric functions are not supported. + fn to_canonical_string(&self) -> String { + self.as_atom_view().to_canonical_string() + } + + /// Map the function `f` over all terms. + fn map_terms_single_core(&self, f: impl Fn(AtomView) -> Atom) -> Atom { + self.as_atom_view().map_terms_single_core(f) + } + + /// Map the function `f` over all terms, using parallel execution with `n_cores` cores. + fn map_terms(&self, f: impl Fn(AtomView) -> Atom + Send + Sync, n_cores: usize) -> Atom { + self.as_atom_view().map_terms(f, n_cores) + } + + /// Map the function `f` over all terms, using parallel execution with `n_cores` cores. + fn map_terms_with_pool( + &self, + f: impl Fn(AtomView) -> Atom + Send + Sync, + p: &ThreadPool, + ) -> Atom { + self.as_atom_view().map_terms_with_pool(f, p) + } + + /// Canonize (products of) tensors in the expression by relabeling repeated indices. + /// The tensors must be written as functions, with its indices are the arguments. + /// The repeated indices should be provided in `contracted_indices`. + /// + /// If the contracted indices are distinguishable (for example in their dimension), + /// you can provide an optional group marker for each index using `index_group`. + /// This makes sure that an index will not be renamed to an index from a different group. + /// + /// Example + /// ------- + /// ``` + /// # use symbolica::{atom::{Atom, AtomCore}, state::{FunctionAttribute, State}}; + /// # + /// # fn main() { + /// let _ = State::get_symbol_with_attributes("fs", &[FunctionAttribute::Symmetric]).unwrap(); + /// let _ = State::get_symbol_with_attributes("fc", &[FunctionAttribute::Cyclesymmetric]).unwrap(); + /// let a = Atom::parse("fs(mu2,mu3)*fc(mu4,mu2,k1,mu4,k1,mu3)").unwrap(); + /// + /// let mu1 = Atom::parse("mu1").unwrap(); + /// let mu2 = Atom::parse("mu2").unwrap(); + /// let mu3 = Atom::parse("mu3").unwrap(); + /// let mu4 = Atom::parse("mu4").unwrap(); + /// + /// let r = a.canonize_tensors(&[mu1.as_view(), mu2.as_view(), mu3.as_view(), mu4.as_view()], None).unwrap(); + /// println!("{}", r); + /// # } + /// ``` + /// yields `fs(mu1,mu2)*fc(mu1,k1,mu3,k1,mu2,mu3)`. + fn canonize_tensors( + &self, + contracted_indices: &[AtomView], + index_group: Option<&[AtomView]>, + ) -> Result { + self.as_atom_view() + .canonize_tensors(contracted_indices, index_group) + } + + fn to_pattern(&self) -> Pattern { + Pattern::from_view(self.as_atom_view(), true) + } + + /// Get all symbols in the expression, optionally including function symbols. + fn get_all_symbols(&self, include_function_symbols: bool) -> HashSet { + self.as_atom_view() + .get_all_symbols(include_function_symbols) + } + + /// Get all variables and functions in the expression. + fn get_all_indeterminates<'a>(&'a self, enter_functions: bool) -> HashSet> { + self.as_atom_view().get_all_indeterminates(enter_functions) + } + + /// Returns true iff `self` contains the symbol `s`. + fn contains_symbol(&self, s: Symbol) -> bool { + self.as_atom_view().contains_symbol(s) + } + + /// Returns true iff `self` contains `a` literally. + fn contains(&self, s: T) -> bool { + self.as_atom_view().contains(s.as_atom_view()) + } + + /// Check if the expression can be considered a polynomial in some variables, including + /// redefinitions. For example `f(x)+y` is considered a polynomial in `f(x)` and `y`, whereas + /// `f(x)+x` is not a polynomial. + /// + /// Rational powers or powers in variables are not rewritten, e.g. `x^(2y)` is not considered + /// polynomial in `x^y`. + fn is_polynomial( + &self, + allow_not_expanded: bool, + allow_negative_powers: bool, + ) -> Option>> { + self.as_atom_view() + .is_polynomial(allow_not_expanded, allow_negative_powers) + } + + /// Replace all occurrences of the pattern. + fn replace_all( + &self, + pattern: &Pattern, + rhs: R, + conditions: Option<&Condition>, + settings: Option<&MatchSettings>, + ) -> Atom { + self.as_atom_view() + .replace_all(pattern, rhs, conditions, settings) + } + + /// Replace all occurrences of the pattern. + fn replace_all_into( + &self, + pattern: &Pattern, + rhs: R, + conditions: Option<&Condition>, + settings: Option<&MatchSettings>, + out: &mut Atom, + ) -> bool { + self.as_atom_view() + .replace_all_into(pattern, rhs, conditions, settings, out) + } + + /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. + fn replace_all_multiple(&self, replacements: &[T]) -> Atom { + self.as_atom_view().replace_all_multiple(replacements) + } + + /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. + /// Returns `true` iff a match was found. + fn replace_all_multiple_into( + &self, + replacements: &[T], + out: &mut Atom, + ) -> bool { + self.as_atom_view() + .replace_all_multiple_into(replacements, out) + } + + /// Replace part of an expression by calling the map `m` on each subexpression. + /// The function `m` must return `true` if the expression was replaced and must write the new expression to `out`. + /// A [Context] object is passed to the function, which contains information about the current position in the expression. + fn replace_map bool>(&self, m: &F) -> Atom { + self.as_atom_view().replace_map(m) + } + + /// Return an iterator that replaces the pattern in the target once. + fn replace_iter<'a>( + &'a self, + pattern: &'a Pattern, + rhs: &'a PatternOrMap, + conditions: &'a Condition, + settings: &'a MatchSettings, + ) -> ReplaceIterator<'a, 'a> { + ReplaceIterator::new(pattern, self.as_atom_view(), rhs, conditions, settings) + } + + /// Return an iterator over matched expressions. + fn pattern_match<'a>( + &'a self, + pattern: &'a Pattern, + conditions: &'a Condition, + settings: &'a MatchSettings, + ) -> PatternAtomTreeIterator<'a, 'a> { + PatternAtomTreeIterator::new(pattern, self.as_atom_view(), conditions, settings) + } +} + +impl<'a> AtomCore for AtomView<'a> { + fn as_atom_view(&self) -> AtomView<'a> { + *self + } +} + +impl AtomCore for InlineVar { + fn as_atom_view(&self) -> AtomView { + self.as_view() + } +} + +impl AtomCore for InlineNum { + fn as_atom_view(&self) -> AtomView { + self.as_view() + } +} + +impl> AtomCore for T { + fn as_atom_view(&self) -> AtomView { + self.as_ref().as_view() + } +} + +impl<'a> AtomCore for AtomOrView<'a> { + fn as_atom_view(&self) -> AtomView { + self.as_view() + } +} diff --git a/src/coefficient.rs b/src/coefficient.rs index b6982def..06fb5aec 100644 --- a/src/coefficient.rs +++ b/src/coefficient.rs @@ -1093,51 +1093,9 @@ impl<'a> TryFrom> for Float { } } -impl Atom { - /// Set the coefficient ring to the multivariate rational polynomial with `vars` variables. - pub fn set_coefficient_ring(&self, vars: &Arc>) -> Atom { - self.as_view().set_coefficient_ring(vars) - } - - /// Convert all coefficients to floats with a given precision `decimal_prec``. - /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. - pub fn coefficients_to_float(&self, decimal_prec: u32) -> Atom { - let mut a = Atom::new(); - self.as_view() - .coefficients_to_float_into(decimal_prec, &mut a); - a - } - - /// Convert all coefficients to floats with a given precision `decimal_prec``. - /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. - pub fn coefficients_to_float_into(&self, decimal_prec: u32, out: &mut Atom) { - self.as_view().coefficients_to_float_into(decimal_prec, out); - } - - /// Map all coefficients using a given function. - pub fn map_coefficient Coefficient + Copy>(&self, f: F) -> Atom { - self.as_view().map_coefficient(f) - } - - /// Map all coefficients using a given function. - pub fn map_coefficient_into Coefficient + Copy>( - &self, - f: F, - out: &mut Atom, - ) { - self.as_view().map_coefficient_into(f, out); - } - - /// Map all floating point and rational coefficients to the best rational approximation - /// in the interval `[self*(1-relative_error),self*(1+relative_error)]`. - pub fn rationalize_coefficients(&self, relative_error: &Rational) -> Atom { - self.as_view().rationalize_coefficients(relative_error) - } -} - impl<'a> AtomView<'a> { /// Set the coefficient ring to the multivariate rational polynomial with `vars` variables. - pub fn set_coefficient_ring(&self, vars: &Arc>) -> Atom { + pub(crate) fn set_coefficient_ring(&self, vars: &Arc>) -> Atom { Workspace::get_local().with(|ws| { let mut out = ws.new_atom(); self.set_coefficient_ring_with_ws_into(vars, ws, &mut out); @@ -1146,7 +1104,7 @@ impl<'a> AtomView<'a> { } /// Set the coefficient ring to the multivariate rational polynomial with `vars` variables. - pub fn set_coefficient_ring_with_ws_into( + pub(crate) fn set_coefficient_ring_with_ws_into( &self, vars: &Arc>, workspace: &Workspace, @@ -1312,14 +1270,7 @@ impl<'a> AtomView<'a> { /// Convert all coefficients to floats with a given precision `decimal_prec``. /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. - pub fn coefficients_to_float(&self, decimal_prec: u32) -> Atom { - let mut a = Atom::new(); - self.coefficients_to_float_into(decimal_prec, &mut a); - a - } - /// Convert all coefficients to floats with a given precision `decimal_prec``. - /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`. - pub fn coefficients_to_float_into(&self, decimal_prec: u32, out: &mut Atom) { + pub(crate) fn coefficients_to_float_into(&self, decimal_prec: u32, out: &mut Atom) { let binary_prec = (decimal_prec as f64 * LOG2_10).ceil() as u32; Workspace::get_local().with(|ws| self.to_float_impl(binary_prec, true, false, ws, out)) @@ -1442,7 +1393,7 @@ impl<'a> AtomView<'a> { /// Map all floating point and rational coefficients to the best rational approximation /// in the interval `[self*(1-relative_error),self*(1+relative_error)]`. - pub fn rationalize_coefficients(&self, relative_error: &Rational) -> Atom { + pub(crate) fn rationalize_coefficients(&self, relative_error: &Rational) -> Atom { let mut a = Atom::new(); Workspace::get_local().with(|ws| { self.map_coefficient_impl( @@ -1467,14 +1418,17 @@ impl<'a> AtomView<'a> { } /// Map all coefficients using a given function. - pub fn map_coefficient Coefficient + Copy>(&self, f: F) -> Atom { + pub(crate) fn map_coefficient Coefficient + Copy>( + &self, + f: F, + ) -> Atom { let mut a = Atom::new(); self.map_coefficient_into(f, &mut a); a } /// Map all coefficients using a given function. - pub fn map_coefficient_into Coefficient + Copy>( + pub(crate) fn map_coefficient_into Coefficient + Copy>( &self, f: F, out: &mut Atom, @@ -1574,7 +1528,7 @@ mod test { use std::sync::Arc; use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::float::Float, printer::{AtomPrinter, PrintOptions}, state::State, diff --git a/src/collect.rs b/src/collect.rs index 8803067b..719f4c28 100644 --- a/src/collect.rs +++ b/src/collect.rs @@ -1,5 +1,5 @@ use crate::{ - atom::{Add, AsAtomView, Atom, AtomView, Symbol}, + atom::{Add, Atom, AtomCore, AtomView, Symbol}, coefficient::{Coefficient, CoefficientView}, domains::{integer::Z, rational::Q}, poly::{factor::Factorize, polynomial::MultivariatePolynomial, Exponent}, @@ -7,83 +7,6 @@ use crate::{ }; use std::sync::Arc; -impl Atom { - /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g. - /// - /// ```math - /// collect(x + x * y + x^2, x) = x * (1+y) + x^2 - /// ``` - /// - /// Both the *key* (the quantity collected in) and its coefficient can be mapped using - /// `key_map` and `coeff_map` respectively. - pub fn collect( - &self, - x: T, - key_map: Option>, - coeff_map: Option>, - ) -> Atom { - self.as_view().collect::(x, key_map, coeff_map) - } - - /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g. - /// - /// ```math - /// collect(x + x * y + x^2, x) = x * (1+y) + x^2 - /// ``` - /// - /// Both the *key* (the quantity collected in) and its coefficient can be mapped using - /// `key_map` and `coeff_map` respectively. - pub fn collect_multiple( - &self, - xs: &[T], - key_map: Option>, - coeff_map: Option>, - ) -> Atom { - self.as_view() - .collect_multiple::(xs, key_map, coeff_map) - } - - /// Collect terms involving the same power of `x` in `xs`, where `xs` is a list of indeterminates. - /// Return the list of key-coefficient pairs - pub fn coefficient_list(&self, xs: &[T]) -> Vec<(Atom, Atom)> { - self.as_view().coefficient_list::(xs) - } - - /// Collect terms involving the literal occurrence of `x`. - pub fn coefficient(&self, x: T) -> Atom { - Workspace::get_local().with(|ws| self.as_view().coefficient_with_ws(x.as_atom_view(), ws)) - } - - /// Write the expression over a common denominator. - pub fn together(&self) -> Atom { - self.as_view().together() - } - - /// Write the expression as a sum of terms with minimal denominators. - pub fn apart(&self, x: Symbol) -> Atom { - self.as_view().apart(x) - } - - /// Cancel all common factors between numerators and denominators. - /// Any non-canceling parts of the expression will not be rewritten. - pub fn cancel(&self) -> Atom { - self.as_view().cancel() - } - - /// Factor the expression over the rationals. - pub fn factor(&self) -> Atom { - self.as_view().factor() - } - - /// Collect numerical factors by removing the numerical content from additions. - /// For example, `-2*x + 4*x^2 + 6*x^3` will be transformed into `-2*(x - 2*x^2 - 3*x^3)`. - /// - /// The first argument of the addition is normalized to a positive quantity. - pub fn collect_num(&self) -> Atom { - self.as_view().collect_num() - } -} - impl<'a> AtomView<'a> { /// Collect terms involving the same power of `x`, where `x` is an indeterminate, e.g. /// @@ -93,7 +16,7 @@ impl<'a> AtomView<'a> { /// /// Both the *key* (the quantity collected in) and its coefficient can be mapped using /// `key_map` and `coeff_map` respectively. - pub fn collect( + pub(crate) fn collect( &self, x: T, key_map: Option>, @@ -102,7 +25,7 @@ impl<'a> AtomView<'a> { self.collect_multiple::(std::slice::from_ref(&x), key_map, coeff_map) } - pub fn collect_multiple( + pub(crate) fn collect_multiple( &self, xs: &[T], key_map: Option>, @@ -114,7 +37,7 @@ impl<'a> AtomView<'a> { out } - pub fn collect_multiple_impl( + pub(crate) fn collect_multiple_impl( &self, xs: &[T], ws: &Workspace, @@ -166,7 +89,7 @@ impl<'a> AtomView<'a> { /// Collect terms involving the same powers of `x` in `xs`, where `x` is an indeterminate. /// Return the list of key-coefficient pairs. - pub fn coefficient_list(&self, xs: &[T]) -> Vec<(Atom, Atom)> { + pub(crate) fn coefficient_list(&self, xs: &[T]) -> Vec<(Atom, Atom)> { let vars = xs .iter() .map(|x| x.as_atom_view().to_owned().into()) @@ -190,11 +113,6 @@ impl<'a> AtomView<'a> { coeffs } - /// Collect terms involving the literal occurrence of `x`. - pub fn coefficient(&self, x: T) -> Atom { - Workspace::get_local().with(|ws| self.coefficient_with_ws(x.as_atom_view(), ws)) - } - /// Collect terms involving the literal occurrence of `x`. pub fn coefficient_with_ws(&self, x: AtomView<'_>, workspace: &Workspace) -> Atom { let mut coeffs = workspace.new_atom(); @@ -312,7 +230,7 @@ impl<'a> AtomView<'a> { /// Cancel all common factors between numerators and denominators. /// Any non-canceling parts of the expression will not be rewritten. - pub fn cancel(&self) -> Atom { + pub(crate) fn cancel(&self) -> Atom { let mut out = Atom::new(); self.cancel_into(&mut out); out @@ -320,7 +238,7 @@ impl<'a> AtomView<'a> { /// Cancel all common factors between numerators and denominators. /// Any non-canceling parts of the expression will not be rewritten. - pub fn cancel_into(&self, out: &mut Atom) { + pub(crate) fn cancel_into(&self, out: &mut Atom) { Workspace::get_local().with(|ws| { self.cancel_with_ws_into(ws, out); }); @@ -656,7 +574,7 @@ impl<'a> AtomView<'a> { #[cfg(test)] mod test { use crate::{ - atom::{representation::InlineVar, Atom}, + atom::{representation::InlineVar, Atom, AtomCore}, fun, state::State, }; diff --git a/src/derivative.rs b/src/derivative.rs index 413694a8..95005064 100644 --- a/src/derivative.rs +++ b/src/derivative.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - atom::{AsAtomView, Atom, AtomView, FunctionBuilder, Symbol}, + atom::{Atom, AtomView, FunctionBuilder, Symbol}, coefficient::{Coefficient, CoefficientView}, combinatorics::CombinationWithReplacementIterator, domains::{atom::AtomField, integer::Integer, rational::Rational}, @@ -12,35 +12,9 @@ use crate::{ state::Workspace, }; -impl Atom { - /// Take a derivative of the expression with respect to `x`. - pub fn derivative(&self, x: Symbol) -> Atom { - self.as_view().derivative(x) - } - - /// Take a derivative of the expression with respect to `x` and - /// write the result in `out`. - /// Returns `true` if the derivative is non-zero. - pub fn derivative_into(&self, x: Symbol, out: &mut Atom) -> bool { - self.as_view().derivative_into(x, out) - } - - /// Series expand in `x` around `expansion_point` to depth `depth`. - pub fn series( - &self, - x: Symbol, - expansion_point: T, - depth: Rational, - depth_is_absolute: bool, - ) -> Result, &'static str> { - self.as_view() - .series(x, expansion_point.as_atom_view(), depth, depth_is_absolute) - } -} - impl<'a> AtomView<'a> { /// Take a derivative of the expression with respect to `x`. - pub fn derivative(&self, x: Symbol) -> Atom { + pub(crate) fn derivative(&self, x: Symbol) -> Atom { Workspace::get_local().with(|ws| { let mut out = ws.new_atom(); self.derivative_with_ws_into(x, ws, &mut out); @@ -51,14 +25,14 @@ impl<'a> AtomView<'a> { /// Take a derivative of the expression with respect to `x` and /// write the result in `out`. /// Returns `true` if the derivative is non-zero. - pub fn derivative_into(&self, x: Symbol, out: &mut Atom) -> bool { + pub(crate) fn derivative_into(&self, x: Symbol, out: &mut Atom) -> bool { Workspace::get_local().with(|ws| self.derivative_with_ws_into(x, ws, out)) } /// Take a derivative of the expression with respect to `x` and /// write the result in `out`. /// Returns `true` if the derivative is non-zero. - pub fn derivative_with_ws_into( + pub(crate) fn derivative_with_ws_into( &self, x: Symbol, workspace: &Workspace, @@ -762,7 +736,10 @@ impl Sub<&Atom> for &Series { #[cfg(test)] mod test { - use crate::{atom::Atom, state::State}; + use crate::{ + atom::{Atom, AtomCore}, + state::State, + }; #[test] fn derivative() { diff --git a/src/domains/algebraic_number.rs b/src/domains/algebraic_number.rs index a13116ce..753db213 100644 --- a/src/domains/algebraic_number.rs +++ b/src/domains/algebraic_number.rs @@ -644,7 +644,7 @@ impl, E: PositiveExponent> #[cfg(test)] mod tests { - use crate::atom::Atom; + use crate::atom::{Atom, AtomCore}; use crate::domains::algebraic_number::AlgebraicExtension; use crate::domains::finite_field::{PrimeIteratorU64, Zp, Z2}; use crate::domains::rational::Q; diff --git a/src/domains/atom.rs b/src/domains/atom.rs index 71384881..ec3af278 100644 --- a/src/domains/atom.rs +++ b/src/domains/atom.rs @@ -1,5 +1,5 @@ use crate::{ - atom::{Atom, AtomView}, + atom::{Atom, AtomCore, AtomView}, poly::Variable, }; diff --git a/src/domains/finite_field.rs b/src/domains/finite_field.rs index fe1cf82a..b86db16e 100644 --- a/src/domains/finite_field.rs +++ b/src/domains/finite_field.rs @@ -172,7 +172,7 @@ pub trait FiniteFieldCore: Field { /// `m` will be a prime, and the domain will be a field. /// /// [Zp] ([`FiniteField`]) and [Zp64] ([`FiniteField`]) use Montgomery modular arithmetic -/// to increase the performance of the multiplication operator. For the prime `2`, use [Z2] instead. +/// to increase the performance of the multiplication operator. For the prime `2`, use [type@Z2] instead. /// /// For `m` larger than `2^64`, use [`FiniteField`]. /// diff --git a/src/domains/rational.rs b/src/domains/rational.rs index bb5b5ef3..e190c84d 100644 --- a/src/domains/rational.rs +++ b/src/domains/rational.rs @@ -1147,7 +1147,7 @@ impl<'a> std::iter::Sum<&'a Self> for Rational { #[cfg(test)] mod test { use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{ integer::Z, rational::{FractionField, Rational, Q}, diff --git a/src/domains/rational_polynomial.rs b/src/domains/rational_polynomial.rs index 2c7e91bc..34143d8b 100644 --- a/src/domains/rational_polynomial.rs +++ b/src/domains/rational_polynomial.rs @@ -1283,6 +1283,7 @@ mod test { use std::sync::Arc; use crate::{ + atom::AtomCore, domains::{integer::Z, rational::Q, rational_polynomial::RationalPolynomial, Ring}, state::State, }; diff --git a/src/evaluate.rs b/src/evaluate.rs index 48ad50dd..036a5e8b 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -176,78 +176,6 @@ impl Default for OptimizationSettings { } } -impl Atom { - /// Evaluate a (nested) expression a single time. - /// For repeated evaluations, use [Self::evaluator()] and convert - /// to an optimized version or generate a compiled version of your expression. - /// - /// All variables and all user functions in the expression must occur in the map. - pub fn evaluate<'b, T: Real, F: Fn(&Rational) -> T + Copy>( - &'b self, - coeff_map: F, - const_map: &HashMap, T>, - function_map: &HashMap>, - cache: &mut HashMap, T>, - ) -> Result { - self.as_view() - .evaluate(coeff_map, const_map, function_map, cache) - } - - /// Convert nested expressions to a tree suitable for repeated evaluations with - /// different values for `params`. - /// All variables and all user functions in the expression must occur in the map. - pub fn to_evaluation_tree<'a>( - &'a self, - fn_map: &FunctionMap<'a, Rational>, - params: &[Atom], - ) -> Result, String> { - self.as_view().to_evaluation_tree(fn_map, params) - } - - /// Create an efficient evaluator for a (nested) expression. - /// All free parameters must appear in `params` and all other variables - /// and user functions in the expression must occur in the function map. - /// The function map may have nested expressions. - pub fn evaluator<'a>( - &'a self, - fn_map: &FunctionMap<'a, Rational>, - params: &[Atom], - optimization_settings: OptimizationSettings, - ) -> Result, String> { - let mut tree = self.to_evaluation_tree(fn_map, params)?; - Ok(tree.optimize( - optimization_settings.horner_iterations, - optimization_settings.n_cores, - optimization_settings.hot_start.clone(), - optimization_settings.verbose, - )) - } - - /// Convert nested expressions to a tree suitable for repeated evaluations with - /// different values for `params`. - /// All variables and all user functions in the expression must occur in the map. - pub fn evaluator_multiple<'a>( - exprs: &[AtomView<'a>], - fn_map: &FunctionMap<'a, Rational>, - params: &[Atom], - optimization_settings: OptimizationSettings, - ) -> Result, String> { - let mut tree = AtomView::to_eval_tree_multiple(exprs, fn_map, params)?; - Ok(tree.optimize( - optimization_settings.horner_iterations, - optimization_settings.n_cores, - optimization_settings.hot_start.clone(), - optimization_settings.verbose, - )) - } - - /// Check if the expression could be 0, using (potentially) numerical sampling with - /// a given tolerance and number of iterations. - pub fn zero_test(&self, iterations: usize, tolerance: f64) -> ConditionResult { - self.as_view().zero_test(iterations, tolerance) - } -} - #[derive(Debug, Clone)] pub struct SplitExpression { pub tree: Vec>, @@ -4329,7 +4257,7 @@ mod test { use ahash::HashMap; use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{float::Float, rational::Rational}, evaluate::{EvaluationFn, FunctionMap, OptimizationSettings}, id::ConditionResult, diff --git a/src/expand.rs b/src/expand.rs index 5c8931a7..6130c09c 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -3,7 +3,7 @@ use std::{ops::DerefMut, sync::Arc}; use smallvec::SmallVec; use crate::{ - atom::{representation::InlineVar, AsAtomView, Atom, AtomView, Symbol}, + atom::{Atom, AtomView}, coefficient::CoefficientView, combinatorics::CombinationWithReplacementIterator, domains::{integer::Integer, rational::Q}, @@ -11,46 +11,9 @@ use crate::{ state::{RecycledAtom, Workspace}, }; -impl Atom { - /// Expand an expression. The function [expand_via_poly] may be faster. - pub fn expand(&self) -> Atom { - self.as_view().expand() - } - - /// Expand the expression by converting it to a polynomial, optionally - /// only in the indeterminate `var`. The parameter `E` should be a numerical type - /// that fits the largest exponent in the expanded expression. Often, - /// `u8` or `u16` is sufficient. - pub fn expand_via_poly(&self, var: Option) -> Atom { - self.as_view() - .expand_via_poly::(var.as_ref().map(|x| x.as_atom_view())) - } - - /// Expand an expression in the variable `var`. The function [expand_via_poly] may be faster. - pub fn expand_in(&self, var: T) -> Atom { - self.as_view().expand_in(var.as_atom_view()) - } - - /// Expand an expression in the variable `var`. - pub fn expand_in_symbol(&self, var: Symbol) -> Atom { - self.as_view().expand_in(InlineVar::from(var).as_view()) - } - - /// Expand an expression, returning `true` iff the expression changed. - pub fn expand_into(&self, out: &mut Atom) -> bool { - self.as_view().expand_into(None, out) - } - - /// Distribute numbers in the expression, for example: - /// `2*(x+y)` -> `2*x+2*y`. - pub fn expand_num(&self) -> Atom { - self.as_view().expand_num() - } -} - impl<'a> AtomView<'a> { /// Expand an expression. The function [expand_via_poly] may be faster. - pub fn expand(&self) -> Atom { + pub(crate) fn expand(&self) -> Atom { Workspace::get_local().with(|ws| { let mut a = ws.new_atom(); self.expand_with_ws_into(ws, None, &mut a); @@ -59,7 +22,7 @@ impl<'a> AtomView<'a> { } /// Expand an expression. The function [expand_via_poly] may be faster. - pub fn expand_in(&self, var: AtomView) -> Atom { + pub(crate) fn expand_in(&self, var: AtomView) -> Atom { Workspace::get_local().with(|ws| { let mut a = ws.new_atom(); self.expand_with_ws_into(ws, Some(var), &mut a); @@ -68,7 +31,7 @@ impl<'a> AtomView<'a> { } /// Expand an expression, returning `true` iff the expression changed. - pub fn expand_into(&self, var: Option, out: &mut Atom) -> bool { + pub(crate) fn expand_into(&self, var: Option, out: &mut Atom) -> bool { Workspace::get_local().with(|ws| self.expand_with_ws_into(ws, var, out)) } @@ -141,7 +104,7 @@ impl<'a> AtomView<'a> { /// only in the indeterminate `var`. The parameter `E` should be a numerical type /// that fits the largest exponent in the expanded expression. Often, /// `u8` or `u16` is sufficient. - pub fn expand_via_poly(&self, var: Option) -> Atom { + pub(crate) fn expand_via_poly(&self, var: Option) -> Atom { let var_map = var.map(|v| Arc::new(vec![v.to_owned().into()])); let mut out = Atom::new(); @@ -432,7 +395,7 @@ impl<'a> AtomView<'a> { /// Distribute numbers in the expression, for example: /// `2*(x+y)` -> `2*x+2*y`. - pub fn expand_num(&self) -> Atom { + pub(crate) fn expand_num(&self) -> Atom { let mut a = Atom::new(); Workspace::get_local().with(|ws| { self.expand_num_impl(ws, &mut a); @@ -440,13 +403,13 @@ impl<'a> AtomView<'a> { a } - pub fn expand_num_into(&self, out: &mut Atom) { + pub(crate) fn expand_num_into(&self, out: &mut Atom) { Workspace::get_local().with(|ws| { self.expand_with_ws_into(ws, None, out); }) } - pub fn expand_num_impl(&self, ws: &Workspace, out: &mut Atom) -> bool { + pub(crate) fn expand_num_impl(&self, ws: &Workspace, out: &mut Atom) -> bool { match self { AtomView::Num(_) | AtomView::Var(_) | AtomView::Fun(_) => { out.set_from_view(self); @@ -549,7 +512,10 @@ impl<'a> AtomView<'a> { #[cfg(test)] mod test { - use crate::{atom::Atom, state::State}; + use crate::{ + atom::{Atom, AtomCore}, + state::State, + }; #[test] fn expand_num() { diff --git a/src/id.rs b/src/id.rs index eecf80eb..f225e1d1 100644 --- a/src/id.rs +++ b/src/id.rs @@ -6,7 +6,7 @@ use dyn_clone::DynClone; use crate::{ atom::{ representation::{InlineVar, ListSlice}, - AsAtomView, Atom, AtomType, AtomView, Num, SliceType, Symbol, + Atom, AtomCore, AtomType, AtomView, Num, SliceType, Symbol, }, state::Workspace, transformer::{Transformer, TransformerError}, @@ -194,121 +194,6 @@ impl<'a> BorrowReplacement for BorrowedReplacement<'a> { } } -impl Atom { - pub fn to_pattern(&self) -> Pattern { - Pattern::from_view(self.as_view(), true) - } - - /// Get all symbols in the expression, optionally including function symbols. - pub fn get_all_symbols(&self, include_function_symbols: bool) -> HashSet { - let mut out = HashSet::default(); - self.as_view() - .get_all_symbols_impl(include_function_symbols, &mut out); - out - } - - /// Get all variables and functions in the expression. - pub fn get_all_indeterminates<'a>(&'a self, enter_functions: bool) -> HashSet> { - let mut out = HashSet::default(); - self.as_view() - .get_all_indeterminates_impl(enter_functions, &mut out); - out - } - - /// Returns true iff `self` contains the symbol `s`. - pub fn contains_symbol(&self, s: Symbol) -> bool { - self.as_view().contains_symbol(s) - } - - /// Returns true iff `self` contains `a` literally. - pub fn contains(&self, s: T) -> bool { - self.as_view().contains(s.as_atom_view()) - } - - /// Check if the expression can be considered a polynomial in some variables, including - /// redefinitions. For example `f(x)+y` is considered a polynomial in `f(x)` and `y`, whereas - /// `f(x)+x` is not a polynomial. - /// - /// Rational powers or powers in variables are not rewritten, e.g. `x^(2y)` is not considered - /// polynomial in `x^y`. - pub fn is_polynomial( - &self, - allow_not_expanded: bool, - allow_negative_powers: bool, - ) -> Option>> { - self.as_view() - .is_polynomial(allow_not_expanded, allow_negative_powers) - } - - /// Replace all occurrences of the pattern. - pub fn replace_all( - &self, - pattern: &Pattern, - rhs: R, - conditions: Option<&Condition>, - settings: Option<&MatchSettings>, - ) -> Atom { - self.as_view() - .replace_all(pattern, rhs, conditions, settings) - } - - /// Replace all occurrences of the pattern. - pub fn replace_all_into( - &self, - pattern: &Pattern, - rhs: R, - conditions: Option<&Condition>, - settings: Option<&MatchSettings>, - out: &mut Atom, - ) -> bool { - self.as_view() - .replace_all_into(pattern, rhs, conditions, settings, out) - } - - /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. - pub fn replace_all_multiple(&self, replacements: &[T]) -> Atom { - self.as_view().replace_all_multiple(replacements) - } - - /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. - /// Returns `true` iff a match was found. - pub fn replace_all_multiple_into( - &self, - replacements: &[T], - out: &mut Atom, - ) -> bool { - self.as_view().replace_all_multiple_into(replacements, out) - } - - /// Replace part of an expression by calling the map `m` on each subexpression. - /// The function `m` must return `true` if the expression was replaced and must write the new expression to `out`. - /// A [Context] object is passed to the function, which contains information about the current position in the expression. - pub fn replace_map bool>(&self, m: &F) -> Atom { - self.as_view().replace_map(m) - } - - /// Return an iterator that replaces the pattern in the target once. - pub fn replace_iter<'a>( - &'a self, - pattern: &'a Pattern, - rhs: &'a PatternOrMap, - conditions: &'a Condition, - settings: &'a MatchSettings, - ) -> ReplaceIterator<'a, 'a> { - ReplaceIterator::new(pattern, self.as_view(), rhs, conditions, settings) - } - - /// Return an iterator over matched expressions. - pub fn pattern_match<'a>( - &'a self, - pattern: &'a Pattern, - conditions: &'a Condition, - settings: &'a MatchSettings, - ) -> PatternAtomTreeIterator<'a, 'a> { - PatternAtomTreeIterator::new(pattern, self.as_view(), conditions, settings) - } -} - /// The context of an atom. #[derive(Clone, Copy, Debug)] pub struct Context { @@ -321,18 +206,22 @@ pub struct Context { } impl<'a> AtomView<'a> { - pub fn into_pattern(self) -> Pattern { + pub(crate) fn to_pattern(self) -> Pattern { Pattern::from_view(self, true) } /// Get all symbols in the expression, optionally including function symbols. - pub fn get_all_symbols(&self, include_function_symbols: bool) -> HashSet { + pub(crate) fn get_all_symbols(&self, include_function_symbols: bool) -> HashSet { let mut out = HashSet::default(); self.get_all_symbols_impl(include_function_symbols, &mut out); out } - fn get_all_symbols_impl(&self, include_function_symbols: bool, out: &mut HashSet) { + pub(crate) fn get_all_symbols_impl( + &self, + include_function_symbols: bool, + out: &mut HashSet, + ) { match self { AtomView::Num(_) => {} AtomView::Var(v) => { @@ -365,7 +254,7 @@ impl<'a> AtomView<'a> { } /// Get all variables and functions in the expression. - pub fn get_all_indeterminates(&self, enter_functions: bool) -> HashSet> { + pub(crate) fn get_all_indeterminates(&self, enter_functions: bool) -> HashSet> { let mut out = HashSet::default(); self.get_all_indeterminates_impl(enter_functions, &mut out); out @@ -405,7 +294,7 @@ impl<'a> AtomView<'a> { } /// Returns true iff `self` contains `a` literally. - pub fn contains(&self, a: T) -> bool { + pub(crate) fn contains(&self, a: T) -> bool { let mut stack = Vec::with_capacity(20); stack.push(*self); @@ -443,7 +332,7 @@ impl<'a> AtomView<'a> { } /// Returns true iff `self` contains the symbol `s`. - pub fn contains_symbol(&self, s: Symbol) -> bool { + pub(crate) fn contains_symbol(&self, s: Symbol) -> bool { let mut stack = Vec::with_capacity(20); stack.push(*self); while let Some(c) = stack.pop() { @@ -489,7 +378,7 @@ impl<'a> AtomView<'a> { /// /// Rational powers or powers in variables are not rewritten, e.g. `x^(2y)` is not considered /// polynomial in `x^y`. - pub fn is_polynomial( + pub(crate) fn is_polynomial( &self, allow_not_expanded: bool, allow_negative_powers: bool, @@ -630,7 +519,7 @@ impl<'a> AtomView<'a> { /// Replace part of an expression by calling the map `m` on each subexpression. /// The function `m` must return `true` if the expression was replaced and must write the new expression to `out`. /// A [Context] object is passed to the function, which contains information about the current position in the expression. - pub fn replace_map bool>(&self, m: &F) -> Atom { + pub(crate) fn replace_map bool>(&self, m: &F) -> Atom { let mut out = Atom::new(); self.replace_map_into(m, &mut out); out @@ -639,7 +528,7 @@ impl<'a> AtomView<'a> { /// Replace part of an expression by calling the map `m` on each subexpression. /// The function `m` must return `true` if the expression was replaced and must write the new expression to `out`. /// A [Context] object is passed to the function, which contains information about the current position in the expression. - pub fn replace_map_into bool>( + pub(crate) fn replace_map_into bool>( &self, m: &F, out: &mut Atom, @@ -756,7 +645,7 @@ impl<'a> AtomView<'a> { } /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. - pub fn replace_all( + pub(crate) fn replace_all( &self, pattern: &Pattern, rhs: R, @@ -769,7 +658,7 @@ impl<'a> AtomView<'a> { } /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. - pub fn replace_all_into( + pub(crate) fn replace_all_into( &self, pattern: &Pattern, rhs: R, @@ -783,7 +672,7 @@ impl<'a> AtomView<'a> { } /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. - pub fn replace_all_multiple(&self, replacements: &[T]) -> Atom { + pub(crate) fn replace_all_multiple(&self, replacements: &[T]) -> Atom { let mut out = Atom::new(); self.replace_all_multiple_into(replacements, &mut out); out @@ -791,7 +680,7 @@ impl<'a> AtomView<'a> { /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given. /// Returns `true` iff a match was found. - pub fn replace_all_multiple_into( + pub(crate) fn replace_all_multiple_into( &self, replacements: &[T], out: &mut Atom, @@ -1031,29 +920,9 @@ impl<'a> AtomView<'a> { submatch } - /// Return an iterator that replaces the pattern in the target once. - pub fn replace_iter( - &self, - pattern: &'a Pattern, - rhs: &'a PatternOrMap, - conditions: &'a Condition, - settings: &'a MatchSettings, - ) -> ReplaceIterator<'a, 'a> { - ReplaceIterator::new(pattern, *self, rhs, conditions, settings) - } - - pub fn pattern_match( - &self, - pattern: &'a Pattern, - conditions: &'a Condition, - settings: &'a MatchSettings, - ) -> PatternAtomTreeIterator<'a, 'a> { - PatternAtomTreeIterator::new(pattern, *self, conditions, settings) - } - /// Replace all occurrences of the pattern in the target, returning `true` iff a match was found. /// For every matched atom, the first canonical match is used and then the atom is skipped. - pub fn replace_all_with_ws_into( + pub(crate) fn replace_all_with_ws_into( &self, pattern: &Pattern, rhs: BorrowedPatternOrMap, @@ -1388,7 +1257,7 @@ impl Pattern { } /// Create a pattern from an atom view. - fn from_view(atom: AtomView<'_>, is_top_layer: bool) -> Pattern { + pub(crate) fn from_view(atom: AtomView<'_>, is_top_layer: bool) -> Pattern { // split up Add and Mul for literal patterns as well so that x+y can match to x+y+z if Self::has_wildcard(atom) || is_top_layer && matches!(atom, AtomView::Mul(_) | AtomView::Add(_)) @@ -1956,7 +1825,7 @@ impl Evaluate for Relation { let c = Condition::default(); let s = MatchSettings::default(); let m = MatchStack::new(&c, &s); - let pat = state.map(|x| x.into_pattern()); + let pat = state.map(|x| x.to_pattern()); Ok(match self { Relation::Eq(a, b) @@ -3702,7 +3571,7 @@ impl<'a: 'b, 'b> ReplaceIterator<'a, 'b> { #[cfg(test)] mod test { use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, id::{ ConditionResult, MatchSettings, PatternOrMap, PatternRestriction, Replacement, WildcardRestriction, diff --git a/src/lib.rs b/src/lib.rs index 6e62af7d..cc5a01cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ //! For example: //! //! ``` -//! use symbolica::{atom::Atom, state::State}; +//! use symbolica::{atom::Atom, atom::AtomCore, state::State}; //! //! fn main() { //! let input = Atom::parse("x^2*log(2*x + y) + exp(3*x)").unwrap(); diff --git a/src/normalize.rs b/src/normalize.rs index cfb2653c..06e13ac8 100644 --- a/src/normalize.rs +++ b/src/normalize.rs @@ -512,7 +512,7 @@ impl Atom { /// Merge two terms if possible. If this function returns `true`, `self` /// will have been updated by the merge from `other` and `other` should be discarded. /// If the function return `false`, no merge was possible and no modifications were made. - pub fn merge_terms(&mut self, other: AtomView, helper: &mut Self) -> bool { + pub(crate) fn merge_terms(&mut self, other: AtomView, helper: &mut Self) -> bool { if let Atom::Num(n1) = self { if let AtomView::Num(n2) = other { n1.add(&n2); diff --git a/src/parser.rs b/src/parser.rs index 322e223e..9be03c6e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1298,7 +1298,7 @@ mod test { use std::sync::Arc; use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::integer::Z, parser::Token, printer::{AtomPrinter, PrintOptions}, diff --git a/src/poly.rs b/src/poly.rs index 1832ce56..1841ef52 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -19,7 +19,7 @@ use ahash::HashMap; use smallvec::{smallvec, SmallVec}; use smartstring::{LazyCompact, SmartString}; -use crate::atom::{Atom, AtomView, Symbol}; +use crate::atom::{Atom, AtomCore, AtomView, Symbol}; use crate::coefficient::{Coefficient, CoefficientView, ConvertToRing}; use crate::domains::atom::AtomField; use crate::domains::factorized_rational_polynomial::{ @@ -735,77 +735,6 @@ impl Variable { } } -impl Atom { - /// Convert the atom to a polynomial, optionally in the variable ordering - /// specified by `var_map`. If new variables are encountered, they are - /// added to the variable map. Similarly, non-polynomial parts are automatically - /// defined as a new independent variable in the polynomial. - pub fn to_polynomial( - &self, - field: &R, - var_map: Option>>, - ) -> MultivariatePolynomial { - self.as_view().to_polynomial(field, var_map) - } - - /// Convert the atom to a polynomial in specific variables. - /// All other parts will be collected into the coefficient, which - /// is a general expression. - /// - /// This routine does not perform expansions. - pub fn to_polynomial_in_vars( - &self, - var_map: &Arc>, - ) -> MultivariatePolynomial { - self.as_view().to_polynomial_in_vars(var_map) - } - - /// Convert the atom to a rational polynomial, optionally in the variable ordering - /// specified by `var_map`. If new variables are encountered, they are - /// added to the variable map. Similarly, non-rational polynomial parts are automatically - /// defined as a new independent variable in the rational polynomial. - pub fn to_rational_polynomial< - R: EuclideanDomain + ConvertToRing, - RO: EuclideanDomain + PolynomialGCD, - E: PositiveExponent, - >( - &self, - field: &R, - out_field: &RO, - var_map: Option>>, - ) -> RationalPolynomial - where - RationalPolynomial: - FromNumeratorAndDenominator + FromNumeratorAndDenominator, - { - self.as_view() - .to_rational_polynomial(field, out_field, var_map) - } - - /// Convert the atom to a rational polynomial with factorized denominators, optionally in the variable ordering - /// specified by `var_map`. If new variables are encountered, they are - /// added to the variable map. Similarly, non-rational polynomial parts are automatically - /// defined as a new independent variable in the rational polynomial. - pub fn to_factorized_rational_polynomial< - R: EuclideanDomain + ConvertToRing, - RO: EuclideanDomain + PolynomialGCD, - E: PositiveExponent, - >( - &self, - field: &R, - out_field: &RO, - var_map: Option>>, - ) -> FactorizedRationalPolynomial - where - FactorizedRationalPolynomial: FromNumeratorAndFactorizedDenominator - + FromNumeratorAndFactorizedDenominator, - MultivariatePolynomial: Factorize, - { - self.as_view() - .to_factorized_rational_polynomial(field, out_field, var_map) - } -} - impl<'a> AtomView<'a> { /// Convert an expanded expression to a polynomial. fn to_polynomial_expanded( @@ -1013,7 +942,7 @@ impl<'a> AtomView<'a> { /// specified by `var_map`. If new variables are encountered, they are /// added to the variable map. Similarly, non-polynomial parts are automatically /// defined as a new independent variable in the polynomial. - pub fn to_polynomial( + pub(crate) fn to_polynomial( &self, field: &R, var_map: Option>>, @@ -1021,7 +950,7 @@ impl<'a> AtomView<'a> { self.to_polynomial_impl(field, var_map.as_ref().unwrap_or(&Arc::new(Vec::new()))) } - pub fn to_polynomial_impl( + pub(crate) fn to_polynomial_impl( &self, field: &R, var_map: &Arc>, @@ -1155,7 +1084,7 @@ impl<'a> AtomView<'a> { /// is a general expression. /// /// This routine does not perform expansions. - pub fn to_polynomial_in_vars( + pub(crate) fn to_polynomial_in_vars( &self, var_map: &Arc>, ) -> MultivariatePolynomial { @@ -1260,7 +1189,7 @@ impl<'a> AtomView<'a> { /// specified by `var_map`. If new variables are encountered, they are /// added to the variable map. Similarly, non-rational polynomial parts are automatically /// defined as a new independent variable in the rational polynomial. - pub fn to_rational_polynomial< + pub(crate) fn to_rational_polynomial< R: EuclideanDomain + ConvertToRing, RO: EuclideanDomain + PolynomialGCD, E: PositiveExponent, @@ -1403,7 +1332,7 @@ impl<'a> AtomView<'a> { /// specified by `var_map`. If new variables are encountered, they are /// added to the variable map. Similarly, non-rational polynomial parts are automatically /// defined as a new independent variable in the rational polynomial. - pub fn to_factorized_rational_polynomial< + pub(crate) fn to_factorized_rational_polynomial< R: EuclideanDomain + ConvertToRing, RO: EuclideanDomain + PolynomialGCD, E: PositiveExponent, diff --git a/src/poly/evaluate.rs b/src/poly/evaluate.rs index e9388656..55425fbb 100644 --- a/src/poly/evaluate.rs +++ b/src/poly/evaluate.rs @@ -7,7 +7,12 @@ use ahash::{AHasher, HashMap, HashSet, HashSetExt}; use rand::{thread_rng, Rng}; use crate::{ - atom::Symbol, + atom::{Atom, AtomView}, + domains::{float::Real, Ring}, + evaluate::EvaluationFn, +}; +use crate::{ + atom::{AtomCore, Symbol}, coefficient::CoefficientView, domains::{ float::NumericalFloatLike, @@ -16,11 +21,6 @@ use crate::{ }, state::Workspace, }; -use crate::{ - atom::{Atom, AtomView}, - domains::{float::Real, Ring}, - evaluate::EvaluationFn, -}; use super::{polynomial::MultivariatePolynomial, PositiveExponent}; @@ -1961,7 +1961,7 @@ auto 𝑖 = 1i;\n", #[cfg(test)] mod test { use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{float::Complex, rational::Q}, poly::{ evaluate::{BorrowedHornerScheme, InstructionSetPrinter}, diff --git a/src/poly/factor.rs b/src/poly/factor.rs index aaf9de88..4a981f3a 100644 --- a/src/poly/factor.rs +++ b/src/poly/factor.rs @@ -3398,7 +3398,7 @@ mod test { use std::sync::Arc; use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{ algebraic_number::AlgebraicExtension, finite_field::{Zp, Z2}, diff --git a/src/poly/groebner.rs b/src/poly/groebner.rs index 95c242b1..5f82a737 100644 --- a/src/poly/groebner.rs +++ b/src/poly/groebner.rs @@ -934,7 +934,7 @@ echelonize_impl!(AlgebraicExtension); #[cfg(test)] mod test { use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::finite_field::Zp, poly::{groebner::GroebnerBasis, polynomial::MultivariatePolynomial, GrevLexOrder}, }; diff --git a/src/poly/polynomial.rs b/src/poly/polynomial.rs index 4af53e42..02606936 100755 --- a/src/poly/polynomial.rs +++ b/src/poly/polynomial.rs @@ -3740,7 +3740,11 @@ impl<'a, F: Ring, E: Exponent, O: MonomialOrder> IntoIterator #[cfg(test)] mod test { - use crate::{atom::Atom, domains::integer::Z, state::State}; + use crate::{ + atom::{Atom, AtomCore}, + domains::integer::Z, + state::State, + }; #[test] fn mul_packed() { diff --git a/src/poly/resultant.rs b/src/poly/resultant.rs index 72cb574d..ede08e95 100644 --- a/src/poly/resultant.rs +++ b/src/poly/resultant.rs @@ -206,7 +206,7 @@ impl UnivariatePolynomial { mod test { use std::sync::Arc; - use crate::atom::Atom; + use crate::atom::{Atom, AtomCore}; use crate::domains::integer::Z; use crate::domains::rational::Q; use crate::domains::rational_polynomial::{ diff --git a/src/poly/series.rs b/src/poly/series.rs index b161ca4a..0ffdf237 100644 --- a/src/poly/series.rs +++ b/src/poly/series.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - atom::{Atom, AtomView, FunctionBuilder}, + atom::{Atom, AtomCore, AtomView, FunctionBuilder}, coefficient::CoefficientView, domains::{ atom::AtomField, diff --git a/src/poly/univariate.rs b/src/poly/univariate.rs index fe49da20..09eedc7f 100644 --- a/src/poly/univariate.rs +++ b/src/poly/univariate.rs @@ -1769,7 +1769,7 @@ impl UnivariatePolynomial> { #[cfg(test)] mod test { use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{float::F64, rational::Q}, }; diff --git a/src/printer.rs b/src/printer.rs index 42c34a00..f1c79cbc 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -251,20 +251,6 @@ impl std::fmt::Display for Symbol { } } -impl Atom { - /// Construct a printer for the atom with special options. - pub fn printer<'a>(&'a self, opts: PrintOptions) -> AtomPrinter<'a> { - AtomPrinter::new_with_options(self.as_view(), opts) - } - - /// Print the atom in a form that is unique and independent of any implementation details. - /// - /// Anti-symmetric functions are not supported. - pub fn to_canonical_string(&self) -> String { - self.as_view().to_canonical_string() - } -} - impl<'a> AtomView<'a> { fn fmt_debug(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { @@ -277,7 +263,7 @@ impl<'a> AtomView<'a> { } } - pub fn format( + pub(crate) fn format( &self, fmt: &mut W, opts: &PrintOptions, @@ -294,14 +280,14 @@ impl<'a> AtomView<'a> { } /// Construct a printer for the atom with special options. - pub fn printer(&self, opts: PrintOptions) -> AtomPrinter { + pub(crate) fn printer(&self, opts: PrintOptions) -> AtomPrinter { AtomPrinter::new_with_options(*self, opts) } /// Print the atom in a form that is unique and independent of any implementation details. /// /// Anti-symmetric functions are not supported. - pub fn to_canonical_string(&self) -> String { + pub(crate) fn to_canonical_string(&self) -> String { let mut s = String::new(); self.to_canonical_view_impl(&mut s); s @@ -955,7 +941,7 @@ mod test { use colored::control::ShouldColorize; use crate::{ - atom::Atom, + atom::{Atom, AtomCore}, domains::{finite_field::Zp, integer::Z, SelfRing}, printer::{AtomPrinter, PrintOptions, PrintState}, state::{FunctionAttribute, State}, diff --git a/src/solve.rs b/src/solve.rs index d27c00f4..336c1dc9 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -1,7 +1,7 @@ use std::{ops::Neg, sync::Arc}; use crate::{ - atom::{AsAtomView, Atom, AtomView, Symbol}, + atom::{Atom, AtomCore, AtomView, Symbol}, domains::{ float::{FloatField, Real, SingleFloat}, integer::Z, @@ -14,60 +14,9 @@ use crate::{ tensors::matrix::Matrix, }; -impl Atom { - /// Find the root of a function in `x` numerically over the reals using Newton's method. - pub fn nsolve( - &self, - x: Symbol, - init: N, - prec: N, - max_iterations: usize, - ) -> Result { - self.as_view().nsolve(x, init, prec, max_iterations) - } - - /// Solve a non-linear system numerically over the reals using Newton's method. - pub fn nsolve_system< - N: SingleFloat + Real + PartialOrd + InternalOrdering + Eq + std::hash::Hash, - T: AsAtomView, - >( - system: &[T], - vars: &[Symbol], - init: &[N], - prec: N, - max_iterations: usize, - ) -> Result, String> { - AtomView::nsolve_system(system, vars, init, prec, max_iterations) - } - - /// Solve a system that is linear in `vars`, if possible. - /// Each expression in `system` is understood to yield 0. - pub fn solve_linear_system( - system: &[T1], - vars: &[T2], - ) -> Result, String> { - AtomView::solve_linear_system::(system, vars) - } - - /// Convert a system of linear equations to a matrix representation, returning the matrix - /// and the right-hand side. - pub fn system_to_matrix( - system: &[T1], - vars: &[T2], - ) -> Result< - ( - Matrix>, - Matrix>, - ), - String, - > { - AtomView::system_to_matrix::(system, vars) - } -} - impl<'a> AtomView<'a> { /// Find the root of a function in `x` numerically over the reals using Newton's method. - pub fn nsolve( + pub(crate) fn nsolve( &self, x: Symbol, init: N, @@ -108,9 +57,9 @@ impl<'a> AtomView<'a> { } /// Solve a non-linear system numerically over the reals using Newton's method. - pub fn nsolve_system< + pub(crate) fn nsolve_system< N: SingleFloat + Real + PartialOrd + InternalOrdering + Eq + std::hash::Hash, - T: AsAtomView, + T: AtomCore, >( system: &[T], vars: &[Symbol], @@ -219,7 +168,7 @@ impl<'a> AtomView<'a> { /// Solve a system that is linear in `vars`, if possible. /// Each expression in `system` is understood to yield 0. - pub fn solve_linear_system( + pub(crate) fn solve_linear_system( system: &[T1], vars: &[T2], ) -> Result, String> { @@ -235,7 +184,7 @@ impl<'a> AtomView<'a> { /// Convert a system of linear equations to a matrix representation, returning the matrix /// and the right-hand side. - pub fn system_to_matrix( + pub(crate) fn system_to_matrix( system: &[T1], vars: &[T2], ) -> Result< @@ -347,7 +296,7 @@ mod test { use std::sync::Arc; use crate::{ - atom::{representation::InlineVar, Atom, AtomView}, + atom::{representation::InlineVar, Atom, AtomCore, AtomView}, domains::{ float::{Real, F64}, integer::Z, diff --git a/src/streaming.rs b/src/streaming.rs index 693deaa2..65fff2e4 100644 --- a/src/streaming.rs +++ b/src/streaming.rs @@ -495,21 +495,9 @@ impl TermStreamer { } } -impl Atom { - /// Map the function `f` over all terms. - pub fn map_terms_single_core(&self, f: impl Fn(AtomView) -> Atom) -> Atom { - self.as_view().map_terms_single_core(f) - } - - /// Map the function `f` over all terms, using parallel execution with `n_cores` cores. - pub fn map_terms(&self, f: impl Fn(AtomView) -> Atom + Send + Sync, n_cores: usize) -> Atom { - self.as_view().map_terms(f, n_cores) - } -} - impl<'a> AtomView<'a> { /// Map the function `f` over all terms. - pub fn map_terms_single_core(&self, f: impl Fn(AtomView) -> Atom) -> Atom { + pub(crate) fn map_terms_single_core(&self, f: impl Fn(AtomView) -> Atom) -> Atom { if let AtomView::Add(aa) = self { return Workspace::get_local().with(|ws| { let mut r = ws.new_atom(); @@ -527,7 +515,11 @@ impl<'a> AtomView<'a> { } /// Map the function `f` over all terms, using parallel execution with `n_cores` cores. - pub fn map_terms(&self, f: impl Fn(AtomView) -> Atom + Send + Sync, n_cores: usize) -> Atom { + pub(crate) fn map_terms( + &self, + f: impl Fn(AtomView) -> Atom + Send + Sync, + n_cores: usize, + ) -> Atom { if n_cores < 2 || !LicenseManager::is_licensed() { return self.map_terms_single_core(f); } @@ -545,7 +537,7 @@ impl<'a> AtomView<'a> { } /// Map the function `f` over all terms, using parallel execution with `n_cores` cores. - pub fn map_terms_with_pool( + pub(crate) fn map_terms_with_pool( &self, f: impl Fn(AtomView) -> Atom + Send + Sync, p: &ThreadPool, @@ -593,7 +585,7 @@ mod test { use brotli::CompressorWriter; use crate::{ - atom::{Atom, AtomType}, + atom::{Atom, AtomCore, AtomType}, id::{Pattern, WildcardRestriction}, state::State, streaming::{TermStreamer, TermStreamerConfig}, @@ -648,17 +640,17 @@ mod test { streamer = streamer.map(|x| { x.replace_all( &pattern, - &rhs, - Some( - &( - State::get_symbol("v1_"), - WildcardRestriction::IsAtomType(AtomType::Var), - ) - .into(), - ), - None, - ) - .expand() + &rhs, + Some( + &( + State::get_symbol("v1_"), + WildcardRestriction::IsAtomType(AtomType::Var), + ) + .into(), + ), + None, + ) + .expand() }); streamer.normalize(); diff --git a/src/tensors.rs b/src/tensors.rs index 8d0d5bbe..a20bf082 100644 --- a/src/tensors.rs +++ b/src/tensors.rs @@ -6,45 +6,6 @@ use crate::{ pub mod matrix; -impl Atom { - /// Canonize (products of) tensors in the expression by relabeling repeated indices. - /// The tensors must be written as functions, with its indices are the arguments. - /// The repeated indices should be provided in `contracted_indices`. - /// - /// If the contracted indices are distinguishable (for example in their dimension), - /// you can provide an optional group marker for each index using `index_group`. - /// This makes sure that an index will not be renamed to an index from a different group. - /// - /// Example - /// ------- - /// ``` - /// # use symbolica::{atom::Atom, state::{FunctionAttribute, State}}; - /// # - /// # fn main() { - /// let _ = State::get_symbol_with_attributes("fs", &[FunctionAttribute::Symmetric]).unwrap(); - /// let _ = State::get_symbol_with_attributes("fc", &[FunctionAttribute::Cyclesymmetric]).unwrap(); - /// let a = Atom::parse("fs(mu2,mu3)*fc(mu4,mu2,k1,mu4,k1,mu3)").unwrap(); - /// - /// let mu1 = Atom::parse("mu1").unwrap(); - /// let mu2 = Atom::parse("mu2").unwrap(); - /// let mu3 = Atom::parse("mu3").unwrap(); - /// let mu4 = Atom::parse("mu4").unwrap(); - /// - /// let r = a.canonize_tensors(&[mu1.as_view(), mu2.as_view(), mu3.as_view(), mu4.as_view()], None).unwrap(); - /// println!("{}", r); - /// # } - /// ``` - /// yields `fs(mu1,mu2)*fc(mu1,k1,mu3,k1,mu2,mu3)`. - pub fn canonize_tensors( - &self, - contracted_indices: &[AtomView], - index_group: Option<&[AtomView]>, - ) -> Result { - self.as_view() - .canonize_tensors(contracted_indices, index_group) - } -} - impl<'a> AtomView<'a> { /// Canonize (products of) tensors in the expression by relabeling repeated indices. /// The tensors must be written as functions, with its indices are the arguments. @@ -53,7 +14,7 @@ impl<'a> AtomView<'a> { /// If the contracted indices are distinguishable (for example in their dimension), /// you can provide an optional group marker for each index using `index_group`. /// This makes sure that an index will not be renamed to an index from a different group. - pub fn canonize_tensors( + pub(crate) fn canonize_tensors( &self, contracted_indices: &[AtomView], index_group: Option<&[AtomView]>, @@ -461,7 +422,7 @@ impl<'a> AtomView<'a> { #[cfg(test)] mod test { use crate::{ - atom::{representation::InlineVar, Atom}, + atom::{representation::InlineVar, Atom, AtomCore}, state::State, }; diff --git a/tests/pattern_matching.rs b/tests/pattern_matching.rs index e511ed2a..2ff70a18 100644 --- a/tests/pattern_matching.rs +++ b/tests/pattern_matching.rs @@ -1,5 +1,5 @@ use symbolica::{ - atom::{Atom, AtomView}, + atom::{Atom, AtomCore, AtomView}, id::{Condition, Match, MatchSettings, Pattern, WildcardRestriction}, state::{RecycledAtom, State}, }; @@ -31,7 +31,7 @@ fn fibonacci() { target.replace_all_into(&pattern, &rhs, Some(&restrictions), None, &mut out); let mut out2 = RecycledAtom::new(); - out.expand_into(&mut out2); + out.expand_into(None, &mut out2); out2.replace_all_into(&lhs_zero_pat, &rhs_one, None, None, &mut out); @@ -49,9 +49,9 @@ fn replace_once() { let pat_expr = Atom::parse("f(x_)").unwrap(); let rhs_expr = Atom::parse("g(x_)").unwrap(); - let rhs = rhs_expr.as_view().into_pattern().into(); + let rhs = rhs_expr.as_view().to_pattern().into(); - let pattern = pat_expr.as_view().into_pattern(); + let pattern = pat_expr.as_view().to_pattern(); let restrictions = Condition::default(); let settings = MatchSettings::default(); diff --git a/tests/rational_polynomial.rs b/tests/rational_polynomial.rs index ceddb17d..835f7517 100644 --- a/tests/rational_polynomial.rs +++ b/tests/rational_polynomial.rs @@ -1,6 +1,12 @@ use std::sync::Arc; -use symbolica::{atom::Atom, domains::integer::Z, parser::Token, poly::Variable, state::State}; +use symbolica::{ + atom::{Atom, AtomCore}, + domains::integer::Z, + parser::Token, + poly::Variable, + state::State, +}; #[test] fn large_gcd_single_scale() {