Common features
Below we list some common features of Symbolica.
Expanding
Symbolica can expand an expression:
from symbolica import Expression
= Expression.symbol('x')
x = (1+x)^2
e print(e.expand())
use symbolica::atom::Atom;
fn main() {
let e = Atom::parse("(1+x)^2").unwrap();
println!("{}", e.expand(None));
}
yielding
x^2+2x+1
Expansions can also be performed in a particular variable:
from symbolica import Expression
= Expression.symbols('x', 'y')
x, y = (1+y)^100 + (1+x)^2
e print(e.expand(x))
use symbolica::atom::Atom;
use symbolica::state::State;
fn main() {
let x = State::get_symbol("x");
let e = Atom::parse("(1+y)^100 + (1+x)^2").unwrap();
println!("{}", e.expand(Some(x)));
}
yielding
x^2+2x+1 + (1+y)^100
Collecting
A coefficient list in a variable can be obtained:
from symbolica import Expression
= Expression.symbols('x', 'y')
x, y = 5*x + x * y + x**2 + 5
e
= e.coefficient_list(x)
l for key, value in l:
print(key, value)
use symbolica::atom::Atom;
fn main() {
let x_symb = Atom::get_symbol("x");
let e = Atom::parse("5*x + x * y + x**2 + 5").unwrap();
for (k, v) in e.coefficient_list(x_symb).0 {
println!("{}, {}", k, v);
}
}
yielding
x^2 1
x y+5
1 5
You can also collect in a variable:
from symbolica import Expression
= Expression.symbols('x', 'y')
x, y = 5*x + x * y + x**2 + 5
e
print(e.collect(x))
use symbolica::atom::Atom;
fn main() {
let x_symb = Atom::get_symbol("x");
let e = Atom::parse("5*x + x * y + x**2 + 5").unwrap();
println!("{}", e.collect(x_symb, None, None));
}
yielding
x^2+x*(y+5)+5
You can also provide a user-defined function to map the key and one to map the coefficient. For example:
from symbolica import Expression
= Expression.symbols('x', 'y')
x, y = Expression.symbols('var', 'coeff')
var, coeff
= 5*x + x * y + x**2 + 5
e
= e.collect(x, key_map=lambda x: symbol(x), coeff_map=lambda x: coeff(x))
e print(e)
use symbolica::{
,
funatom::{Atom, FunctionBuilder},
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();
let x = State::get_symbol("x");
let key = State::get_symbol("key");
let coeff = State::get_symbol("coeff");
println!("> Collect in x with wrapping:");
let out = input.collect(
,
xSome(Box::new(move |a, out| {
.set_from_view(&a);
out*out = fun!(key, out);
})),
Some(Box::new(move |a, out| {
.set_from_view(&a);
out*out = fun!(coeff, out);
})),
;
)println!("\t{}", out);
}
yielding symbol(1)*coeff(5)+symbol(x)*coeff(y+5)+symbol(x^2)*coeff(1)
Together
To write an expression using a common denominator, use together
:
from symbolica import Expression
= Expression.symbols('x', 'y', 'z')
x, y, z = (1/x + (2*x+y+z)/y).together()
e print(e)
use symbolica::{atom::Atom};
fn main() {
let input = Atom::parse("1/x + (2*x+y+z)/y").unwrap();
println!("{}", input.together());
}
yielding
(x*y)^-1*(y+2*x^2+x*y+x*z)
Apart
To apply partial fraction decomposition in a variable x
, use apart(x)
:
from symbolica import Expression
= Expression.symbols('x', 'y', 'z')
x, y, z = ((y+2*x**2+x*y+x*z)/(x*y)).apart(x)
e print(e)
use symbolica::{atom::Atom, state::State};
fn main() {
let input = Atom::parse("(y+2*x^2+x*y+x*z)/(x*y)").unwrap();
println!("{}", input.apart(State::get_symbol("x")));
}
yielding
x^-1+y^-1*(2*x+y+z)
Derivatives
Symbolica can derive expressions with built-in and user-defined functions:
from symbolica import Expression
= Expression.symbol('x')
x
print(Expression.parse("(1+2*x)^(5+x)").derivative(x))
print(Expression.parse("log(2*x) + exp(3*x) + sin(4*x) + cos(y*x)").derivative(x))
print(Expression.parse("f(x^2,x)").derivative(x))
print(Expression.parse("der(0,1,f(x,x^3))").derivative(x))
use symbolica::{atom::Atom, state::State};
fn main() {
let x = State::get_symbol("x");
let inputs = [
"(1+2*x)^(5+x)",
"log(2*x) + exp(3*x) + sin(4*x) + cos(y*x)",
"f(x^2,x)",
"der(0,1,f(x,x^3))",
;
]
for input in inputs {
let input = Atom::parse(input).unwrap();
let a = input.derivative(x);
println!("d({})/dx = {}:", input, a);
}
}
yielding
(2*x+1)^(x+5)*log(2*x+1)+2*(x+5)*(2*x+1)^(x+4)
2*(2*x)^-1+3*exp(3*x)+4*cos(4*x)-y*sin(x*y)
der(0,1,f(x^2,x))+2*x*der(1,0,f(x^2,x))
der(1,1,f(x,x^3))+3*x^2*der(0,2,f(x,x^3))
The built-in der
function keeps counters of the number of derivatives per argument position.
Series expansion
Symbolica can also produce a Puiseux series, with additional support for log(x)
around x=0
. The function series
returns a series object that support efficient arithmetic with other series.
from symbolica import Expression
= Expression.symbol('x')
x = Expression.parse('exp(5+x)/(1-x)').series(x, 0, 3) e
use symbolica::{atom::Atom, state::State};
fn main() {
let x = State::get_symbol("x");
let a = Atom::parse("exp(5+x)/(1-x)").unwrap();
let out = a.series(x, Atom::new().as_view(), 3);
println!("{}", out);
}
yielding
(exp(5))+(2*exp(5))*x+(5/2*exp(5))*x^2+(8/3*exp(5))*x^3+O(x^4)
Solving linear system
Symbolica can solve linear systems:
from symbolica import Expression
= Expression.symbols('x', 'y', 'c')
x, y, c = Expression.symbol('f')
f
= Expression.solve_linear_system(
x_r, y_r *x + y + c, y + c**2], [x, y])
[f(c)print('x =', x_r, ', y =', y_r)
x = (-c+c^2)*f(c)^-1 , y = -c^2
use symbolica::{
atom::{Atom, AtomView},
state::State,
};
fn main() {
let x = State::get_symbol("x");
let y = State::get_symbol("y");
let eqs = ["f(c)*x + y + c", "y + c**2"];
let atoms: Vec<_> = eqs.iter().map(|e| Atom::parse(e).unwrap()).collect();
let system: Vec<_> = atoms.iter().map(|x| x.as_view()).collect();
let sol = AtomView::solve_linear_system::<u8>(&system, &[x, y]).unwrap();
for (v, s) in ["x", "y"].iter().zip(&sol) {
println!("{} = {}", v, s);
}
}