From 48c5b9932013e40e1781201529cd9c8a2952f04e Mon Sep 17 00:00:00 2001 From: Ben Ruijl Date: Tue, 23 Jul 2024 16:14:06 +0200 Subject: [PATCH] Add sympy-compatible export option - Allow printing atoms in canonical form, independent of implementation details. --- examples/fuel_backend.rs | 14 +-- src/api/cpp.rs | 22 +--- src/api/mathematica.rs | 33 +----- src/api/python.rs | 154 ++++++++++++++---------- src/printer.rs | 248 +++++++++++++++++++++++++++++++-------- symbolica.pyi | 15 +++ 6 files changed, 317 insertions(+), 169 deletions(-) diff --git a/examples/fuel_backend.rs b/examples/fuel_backend.rs index 6bd7a891..8c206057 100644 --- a/examples/fuel_backend.rs +++ b/examples/fuel_backend.rs @@ -43,19 +43,7 @@ fn main() { .collect(), ); - let print_opt = PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, - symmetric_representation_for_finite_field: false, - explicit_rational_polynomial: false, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, - }; + let print_opt = PrintOptions::file(); buffer.clear(); while let Ok(n) = stdin.read_line(&mut buffer) { diff --git a/src/api/cpp.rs b/src/api/cpp.rs index 5db4156e..b123dded 100644 --- a/src/api/cpp.rs +++ b/src/api/cpp.rs @@ -163,17 +163,8 @@ unsafe extern "C" fn simplify( let token = Token::parse(cstr).unwrap(); let opts = PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, - symmetric_representation_for_finite_field: false, explicit_rational_polynomial, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, + ..PrintOptions::file() }; macro_rules! to_rational { @@ -299,17 +290,8 @@ unsafe extern "C" fn simplify_factorized( let token = Token::parse(cstr).unwrap(); let opts = PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, - symmetric_representation_for_finite_field: false, explicit_rational_polynomial, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, + ..PrintOptions::file() }; macro_rules! to_rational { diff --git a/src/api/mathematica.rs b/src/api/mathematica.rs index c1b071c7..8d11745a 100644 --- a/src/api/mathematica.rs +++ b/src/api/mathematica.rs @@ -81,17 +81,8 @@ fn simplify(input: String, prime: i64, explicit_rational_polynomial: bool) -> St RationalPolynomialPrinter { poly: &r, opts: PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, explicit_rational_polynomial, - symmetric_representation_for_finite_field: false, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, + ..PrintOptions::mathematica() }, add_parentheses: false } @@ -114,17 +105,8 @@ fn simplify(input: String, prime: i64, explicit_rational_polynomial: bool) -> St RationalPolynomialPrinter { poly: &rf, opts: PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, explicit_rational_polynomial, - symmetric_representation_for_finite_field: false, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, + ..PrintOptions::mathematica() }, add_parentheses: false } @@ -146,17 +128,8 @@ fn simplify(input: String, prime: i64, explicit_rational_polynomial: bool) -> St RationalPolynomialPrinter { poly: &rf, opts: PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: false, explicit_rational_polynomial, - symmetric_representation_for_finite_field: false, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false, + ..PrintOptions::mathematica() }, add_parentheses: false } diff --git a/src/api/python.rs b/src/api/python.rs index f4791736..d833adb7 100644 --- a/src/api/python.rs +++ b/src/api/python.rs @@ -926,6 +926,7 @@ impl PythonPattern { explicit_rational_polynomial = false, number_thousands_separator = None, multiplication_operator = '*', + double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, latex = false) @@ -940,6 +941,7 @@ impl PythonPattern { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -955,6 +957,7 @@ impl PythonPattern { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -1753,18 +1756,19 @@ impl PythonExpression { /// >>> a = Expression.parse('128378127123 z^(2/3)*w^2/x/y + y^4 + z^34 + x^(x+2)+3/5+f(x,x^2)') /// >>> print(a.pretty_str(number_thousands_separator='_', multiplication_operator=' ')) #[pyo3(signature = - (terms_on_new_line = false, - color_top_level_sum = true, - color_builtin_symbols = true, - print_finite_field = true, - symmetric_representation_for_finite_field = false, - explicit_rational_polynomial = false, - number_thousands_separator = None, - multiplication_operator = '*', - square_brackets_for_function = false, - num_exp_as_superscript = true, - latex = false) - )] + (terms_on_new_line = false, + color_top_level_sum = true, + color_builtin_symbols = true, + print_finite_field = true, + symmetric_representation_for_finite_field = false, + explicit_rational_polynomial = false, + number_thousands_separator = None, + multiplication_operator = '*', + double_star_for_exponentiation = false, + square_brackets_for_function = false, + num_exp_as_superscript = true, + latex = false) + )] pub fn pretty_str( &self, terms_on_new_line: bool, @@ -1775,6 +1779,7 @@ impl PythonExpression { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -1792,6 +1797,7 @@ impl PythonExpression { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -1815,6 +1821,16 @@ impl PythonExpression { )) } + /// Convert the expression into a sympy-parsable string. + /// + /// Examples + /// -------- + /// >>> from sympy import * + /// >>> s = sympy.parse_expr(Expression.parse('x^2+f((1+x)^y)').to_sympy()) + pub fn to_sympy(&self) -> PyResult { + Ok(format!("{}", self.expr.printer(PrintOptions::sympy()))) + } + /// Hash the expression. pub fn __hash__(&self) -> u64 { let mut hasher = ahash::AHasher::default(); @@ -4232,18 +4248,19 @@ impl PythonPolynomial { /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) #[pyo3(signature = - (terms_on_new_line = false, - color_top_level_sum = true, - color_builtin_symbols = true, - print_finite_field = true, - symmetric_representation_for_finite_field = false, - explicit_rational_polynomial = false, - number_thousands_separator = None, - multiplication_operator = '*', - square_brackets_for_function = false, - num_exp_as_superscript = true, - latex = false) - )] + (terms_on_new_line = false, + color_top_level_sum = true, + color_builtin_symbols = true, + print_finite_field = true, + symmetric_representation_for_finite_field = false, + explicit_rational_polynomial = false, + number_thousands_separator = None, + multiplication_operator = '*', + double_star_for_exponentiation = false, + square_brackets_for_function = false, + num_exp_as_superscript = true, + latex = false) + )] pub fn pretty_str( &self, terms_on_new_line: bool, @@ -4254,6 +4271,7 @@ impl PythonPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -4271,6 +4289,7 @@ impl PythonPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -4999,18 +5018,19 @@ impl PythonFiniteFieldPolynomial { /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) #[pyo3(signature = - (terms_on_new_line = false, - color_top_level_sum = true, - color_builtin_symbols = true, - print_finite_field = true, - symmetric_representation_for_finite_field = false, - explicit_rational_polynomial = false, - number_thousands_separator = None, - multiplication_operator = '*', - square_brackets_for_function = false, - num_exp_as_superscript = true, - latex = false) - )] + (terms_on_new_line = false, + color_top_level_sum = true, + color_builtin_symbols = true, + print_finite_field = true, + symmetric_representation_for_finite_field = false, + explicit_rational_polynomial = false, + number_thousands_separator = None, + multiplication_operator = '*', + double_star_for_exponentiation = false, + square_brackets_for_function = false, + num_exp_as_superscript = true, + latex = false) + )] pub fn pretty_str( &self, terms_on_new_line: bool, @@ -5021,6 +5041,7 @@ impl PythonFiniteFieldPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -5038,6 +5059,7 @@ impl PythonFiniteFieldPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -5617,18 +5639,19 @@ impl PythonPrimeTwoPolynomial { /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) #[pyo3(signature = - (terms_on_new_line = false, - color_top_level_sum = true, - color_builtin_symbols = true, - print_finite_field = true, - symmetric_representation_for_finite_field = false, - explicit_rational_polynomial = false, - number_thousands_separator = None, - multiplication_operator = '*', - square_brackets_for_function = false, - num_exp_as_superscript = true, - latex = false) - )] + (terms_on_new_line = false, + color_top_level_sum = true, + color_builtin_symbols = true, + print_finite_field = true, + symmetric_representation_for_finite_field = false, + explicit_rational_polynomial = false, + number_thousands_separator = None, + multiplication_operator = '*', + double_star_for_exponentiation = false, + square_brackets_for_function = false, + num_exp_as_superscript = true, + latex = false) + )] pub fn pretty_str( &self, terms_on_new_line: bool, @@ -5639,6 +5662,7 @@ impl PythonPrimeTwoPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -5656,6 +5680,7 @@ impl PythonPrimeTwoPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -6208,6 +6233,7 @@ impl PythonGaloisFieldPrimeTwoPolynomial { explicit_rational_polynomial = false, number_thousands_separator = None, multiplication_operator = '*', + double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, latex = false) @@ -6222,6 +6248,7 @@ impl PythonGaloisFieldPrimeTwoPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -6239,6 +6266,7 @@ impl PythonGaloisFieldPrimeTwoPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -6787,18 +6815,19 @@ impl PythonGaloisFieldPolynomial { /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) #[pyo3(signature = - (terms_on_new_line = false, - color_top_level_sum = true, - color_builtin_symbols = true, - print_finite_field = true, - symmetric_representation_for_finite_field = false, - explicit_rational_polynomial = false, - number_thousands_separator = None, - multiplication_operator = '*', - square_brackets_for_function = false, - num_exp_as_superscript = true, - latex = false) - )] + (terms_on_new_line = false, + color_top_level_sum = true, + color_builtin_symbols = true, + print_finite_field = true, + symmetric_representation_for_finite_field = false, + explicit_rational_polynomial = false, + number_thousands_separator = None, + multiplication_operator = '*', + double_star_for_exponentiation = false, + square_brackets_for_function = false, + num_exp_as_superscript = true, + latex = false) + )] pub fn pretty_str( &self, terms_on_new_line: bool, @@ -6809,6 +6838,7 @@ impl PythonGaloisFieldPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -6826,6 +6856,7 @@ impl PythonGaloisFieldPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex @@ -7383,6 +7414,7 @@ impl PythonNumberFieldPolynomial { explicit_rational_polynomial = false, number_thousands_separator = None, multiplication_operator = '*', + double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, latex = false) @@ -7397,6 +7429,7 @@ impl PythonNumberFieldPolynomial { explicit_rational_polynomial: bool, number_thousands_separator: Option, multiplication_operator: char, + double_star_for_exponentiation: bool, square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, @@ -7414,6 +7447,7 @@ impl PythonNumberFieldPolynomial { explicit_rational_polynomial, number_thousands_separator, multiplication_operator, + double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, latex diff --git a/src/printer.rs b/src/printer.rs index 35f56e6c..cc7e7edd 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -4,7 +4,8 @@ use colored::Colorize; use crate::{ atom::{ - representation::FunView, AddView, AtomView, MulView, NumView, PowView, Symbol, VarView, + representation::FunView, AddView, Atom, AtomView, MulView, NumView, PowView, Symbol, + VarView, }, coefficient::CoefficientView, domains::{ @@ -26,6 +27,7 @@ pub struct PrintOptions { pub explicit_rational_polynomial: bool, pub number_thousands_separator: Option, pub multiplication_operator: char, + pub double_star_for_exponentiation: bool, pub square_brackets_for_function: bool, pub num_exp_as_superscript: bool, pub latex: bool, @@ -33,7 +35,7 @@ pub struct PrintOptions { impl PrintOptions { /// Print the output in a Mathematica-readable format. - pub fn mathematica() -> PrintOptions { + pub const fn mathematica() -> PrintOptions { Self { terms_on_new_line: false, color_top_level_sum: false, @@ -43,6 +45,7 @@ impl PrintOptions { explicit_rational_polynomial: false, number_thousands_separator: None, multiplication_operator: ' ', + double_star_for_exponentiation: false, square_brackets_for_function: true, num_exp_as_superscript: false, latex: false, @@ -50,7 +53,7 @@ impl PrintOptions { } /// Print the output in a Latex input format. - pub fn latex() -> PrintOptions { + pub const fn latex() -> PrintOptions { Self { terms_on_new_line: false, color_top_level_sum: false, @@ -60,6 +63,7 @@ impl PrintOptions { explicit_rational_polynomial: false, number_thousands_separator: None, multiplication_operator: ' ', + double_star_for_exponentiation: false, square_brackets_for_function: false, num_exp_as_superscript: false, latex: true, @@ -67,7 +71,7 @@ impl PrintOptions { } /// Print the output suitable for a file. - pub fn file() -> PrintOptions { + pub const fn file() -> PrintOptions { Self { terms_on_new_line: false, color_top_level_sum: false, @@ -77,11 +81,20 @@ impl PrintOptions { explicit_rational_polynomial: false, number_thousands_separator: None, multiplication_operator: '*', + double_star_for_exponentiation: false, square_brackets_for_function: false, num_exp_as_superscript: false, latex: false, } } + + /// Print the output in a sympy input format. + pub const fn sympy() -> PrintOptions { + Self { + double_star_for_exponentiation: true, + ..Self::file() + } + } } impl Default for PrintOptions { @@ -95,6 +108,7 @@ impl Default for PrintOptions { explicit_rational_polynomial: false, number_thousands_separator: None, multiplication_operator: '*', + double_star_for_exponentiation: false, square_brackets_for_function: false, num_exp_as_superscript: false, latex: false, @@ -175,6 +189,20 @@ 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 { @@ -202,6 +230,145 @@ impl<'a> AtomView<'a> { AtomView::Add(e) => e.fmt_output(fmt, opts, print_state), } } + + /// Construct a printer for the atom with special options. + pub 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 { + let mut s = String::new(); + self.to_canonical_view_impl(&mut s); + s + } + + fn to_canonical_view_impl(&self, out: &mut String) { + fn add_paren(cur: AtomView, s: AtomView) -> bool { + if let AtomView::Pow(_) = cur { + matches!(s, AtomView::Add(_) | AtomView::Mul(_)) + } else if let AtomView::Mul(_) = cur { + matches!(s, AtomView::Add(_)) + } else { + false + } + } + + match self { + AtomView::Num(_) => write!(out, "{}", self).unwrap(), + AtomView::Var(v) => write!(out, "{}", v.get_symbol()).unwrap(), + AtomView::Fun(f) => { + write!(out, "{}(", f.get_symbol()).unwrap(); + + let mut args = vec![]; + + for x in f.iter() { + let mut arg = String::new(); + x.to_canonical_view_impl(&mut arg); + args.push(arg); + } + + // TODO: anti-symmetric may generate minus sign... + if f.is_symmetric() { + args.sort(); + } + + if f.is_antisymmetric() { + unimplemented!( + "Antisymmetric functions are not supported yet for canonical view" + ); + } + + for (i, arg) in args.iter().enumerate() { + if i > 0 { + write!(out, ",").unwrap(); + } + write!(out, "{}", arg).unwrap(); + } + + write!(out, ")").unwrap(); + } + AtomView::Pow(p) => { + let (b, e) = p.get_base_exp(); + + if add_paren(*self, b) { + write!(out, "(").unwrap(); + b.to_canonical_view_impl(out); + write!(out, ")").unwrap(); + } else { + b.to_canonical_view_impl(out); + } + + if add_paren(*self, e) { + write!(out, "^(").unwrap(); + e.to_canonical_view_impl(out); + write!(out, ")").unwrap(); + } else { + write!(out, "^").unwrap(); + e.to_canonical_view_impl(out); + } + } + AtomView::Mul(m) => { + let mut terms = vec![]; + + for x in m.iter() { + let mut term = if add_paren(*self, x) { + "(".to_string() + } else { + String::new() + }; + + x.to_canonical_view_impl(&mut term); + + if add_paren(*self, x) { + term.push(')'); + } + + terms.push(term); + } + + terms.sort(); + + for (i, term) in terms.iter().enumerate() { + if i > 0 { + write!(out, "*").unwrap(); + } + + write!(out, "{}", term).unwrap(); + } + } + AtomView::Add(a) => { + let mut terms = vec![]; + + for x in a.iter() { + let mut term = if add_paren(*self, x) { + "(".to_string() + } else { + String::new() + }; + + x.to_canonical_view_impl(&mut term); + + if add_paren(*self, x) { + term.push(')'); + } + + terms.push(term); + } + + terms.sort(); + + for (i, term) in terms.iter().enumerate() { + if i > 0 { + write!(out, "+").unwrap(); + } + write!(out, "{}", term).unwrap(); + } + } + } + } } impl<'a> fmt::Debug for AtomView<'a> { @@ -606,7 +773,11 @@ impl<'a> FormattedPrintPow for PowView<'a> { } if !superscript_exponent { - f.write_char('^')?; + if !opts.latex && opts.double_star_for_exponentiation { + f.write_str("**")?; + } else { + f.write_char('^')?; + } } if opts.latex { @@ -863,7 +1034,6 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "({})", PolynomialPrinter { poly: d, - opts: self.opts, } ))?; @@ -872,7 +1042,6 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "({})^{}", PolynomialPrinter { poly: d, - opts: self.opts, }, p @@ -900,7 +1069,6 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "{}", PolynomialPrinter { poly: &self.poly.numerator, - opts: self.opts, } ))?; @@ -909,7 +1077,6 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "({})", PolynomialPrinter { poly: &self.poly.numerator, - opts: self.opts, } ))?; @@ -942,7 +1109,6 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "{}", PolynomialPrinter { poly: d, - opts: self.opts, } )); @@ -969,18 +1135,21 @@ impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<' "({})", PolynomialPrinter { poly: d, - opts: self.opts, } ))?; } else { f.write_fmt(format_args!( - "({})^{}", + "({}){}{}", PolynomialPrinter { poly: d, - opts: self.opts, }, + if self.opts.double_star_for_exponentiation { + "**" + } else { + "^" + }, p ))?; } @@ -1223,6 +1392,8 @@ impl<'a, F: Ring + Display, E: Exponent, O: MonomialOrder> Display if e.to_u32() != 1 { if self.opts.latex { write!(f, "^{{{}}}", e)?; + } else if self.opts.double_star_for_exponentiation { + write!(f, "**{}", e)?; } else { write!(f, "^{}", e)?; } @@ -1327,6 +1498,7 @@ mod test { atom::Atom, domains::{finite_field::Zp, integer::Z}, printer::{AtomPrinter, PolynomialPrinter, PrintOptions}, + state::{FunctionAttribute, State}, }; #[test] @@ -1362,17 +1534,10 @@ mod test { AtomPrinter::new_with_options( a.as_view(), PrintOptions { - terms_on_new_line: true, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: true, - symmetric_representation_for_finite_field: false, - explicit_rational_polynomial: false, number_thousands_separator: Some('_'), multiplication_operator: ' ', - square_brackets_for_function: false, num_exp_as_superscript: true, - latex: false + ..PrintOptions::file() } ) ), @@ -1391,17 +1556,9 @@ mod test { PolynomialPrinter::new_with_options( &a, PrintOptions { - terms_on_new_line: true, - color_top_level_sum: false, - color_builtin_symbols: false, print_finite_field: true, symmetric_representation_for_finite_field: true, - explicit_rational_polynomial: false, - number_thousands_separator: Some('_'), - multiplication_operator: ' ', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false + ..PrintOptions::file() } ) ), @@ -1446,24 +1603,23 @@ mod test { assert_eq!( format!( "{}", - AtomPrinter::new_with_options( - a.as_view(), - PrintOptions { - terms_on_new_line: false, - color_top_level_sum: false, - color_builtin_symbols: false, - print_finite_field: true, - symmetric_representation_for_finite_field: true, - explicit_rational_polynomial: false, - number_thousands_separator: None, - multiplication_operator: '*', - square_brackets_for_function: false, - num_exp_as_superscript: false, - latex: false - } - ) + AtomPrinter::new_with_options(a.as_view(), PrintOptions::file()) ), "(-1)^(x+1)-(1/2)^x" ) } + + #[test] + fn canon() { + let _ = + State::get_symbol_with_attributes("canon_f", &[FunctionAttribute::Symmetric]).unwrap(); + let _ = State::get_symbol("canon_y"); + let _ = State::get_symbol("canon_x"); + + let a = Atom::parse("canon_x^2 + 2*canon_x*canon_y + canon_y^2*(canon_x+canon_y) + canon_f(canon_x,canon_y)").unwrap(); + assert_eq!( + a.to_canonical_string(), + "(canon_x+canon_y)*canon_y^2+2*canon_x*canon_y+canon_f(canon_x,canon_y)+canon_x^2" + ); + } } diff --git a/symbolica.pyi b/symbolica.pyi index 2f75868b..7117f8c8 100644 --- a/symbolica.pyi +++ b/symbolica.pyi @@ -266,6 +266,7 @@ class Expression: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, @@ -293,6 +294,15 @@ class Expression: Yields `$$z^{34}+x^{x+2}+y^{4}+f(x,x^{2})+128378127123 z^{\\frac{2}{3}} w^{2} \\frac{1}{x} \\frac{1}{y}+\\frac{3}{5}$$`. """ + def to_sympy(self) -> str: + """Convert the expression into a sympy-parsable string. + + Examples + -------- + >>> from sympy import * + >>> s = sympy.parse_expr(Expression.parse('x^2+f((1+x)^y)').to_sympy()) + """ + def __hash__(self) -> str: """ Hash the expression. @@ -1444,6 +1454,7 @@ class Transformer: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, @@ -1735,6 +1746,7 @@ class Polynomial: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, @@ -1962,6 +1974,7 @@ class IntegerPolynomial: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, @@ -2122,6 +2135,7 @@ class NumberFieldPolynomial: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, @@ -2322,6 +2336,7 @@ class FiniteFieldPolynomial: explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = "*", + double_star_for_exponentiation: bool = False, square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False,