Coefficients
By default a coefficient of a term in Symbolica is a rational number. For example in the expression
\[ 3 x \epsilon + \frac{1}{2}x \]
the coefficients are \(3\) and \(\frac{1}{2}\).
For many applications it is favourable to extend the coefficient ring to include variables. If we consider \(\epsilon\) to be part of the coefficient, we get:
\[ \frac{1 + 6 \epsilon}{2} x \]
For example, when coding the following iterative replacement rule:
\[ I(n) = I(n-1) \frac{2 + (4-2 \epsilon) - 2 n}{2(n - 1) m^2}\,; n \geq 1 \]
a possible Symbolica code is:
from symbolica import *
= Expression.symbol('ep', 'm', 'n_')
ep, m, n_ = Expression.symbol('topo')
topo = Transformer()
T
= topo(10).transform().repeat(
e 'ibp',
T.stats(
T.expand().
replace_all(topo(n_),- 1) * (2 + (4-2*ep) - 2 * n_) /
topo(n_ 2*(n_ - 1)) / m**2,
(1)))
n_.req_gt(
).execute()print(e)
which yields
1/72*ep*m^-18*topo(1)+223/10080*ep^2*m^-18*topo(1)+1/5670*ep^3*m^-18*topo(1)
-101/5760*ep^4*m^-18*topo(1)-229/17280*ep^5*m^-18*topo(1)-13/2880*ep^6*m^-18*topo(1)
-7/8640*ep^7*m^-18*topo(1)-1/13440*ep^8*m^-18*topo(1)-1/362880*ep^9*m^-18*topo(1)
At each stage, the pattern matcher will match and replace a topo(n)
in every term, and will create new terms for every power of ep
. This overhead can be avoided by considering ep
to be a part of the coefficient. Then we have topo(2)*ep+topo(2) = topo(2)*(ep+1)
.
We use Expression.COEFF
(State::COEFF
in Rust) that converts its rational polynomial argument to a coefficient:
= topo(10).transform().repeat(
e 'ibp_num',
T.stats(
T.replace_all(topo(n_),- 1)
topo(n_ *Expression.COEFF((2 + (4-2*ep) -
2 * n_)/(2*(n_ - 1))) / m**2,
1)))
n_.req_gt(
).execute()print(e)
This code is much faster and the expression looks like:
[(5040*ep+8028*ep^2+64*ep^3-6363*ep^4-4809*ep^5-1638*ep^6-294*ep^7-27*ep^8-ep^9)/362880]*m^-18*topo(1)
where [...]
indicates that the contained rational polynomial is a coefficient.
Since it is a coefficient, we cannot pattern match on the internal structure. To do these operations, we can convert back from a number to an atom using from_coeff
:
= e.replace_all(n_, n_.transform().from_coeff(), n_.req_type(AtomType.Num)) e
Now we get:
1/362880*m^-18*(5040*ep+8028*ep^2+64*ep^3-6363*ep^4-4809*ep^5-1638*ep^6-294*ep^7-27*ep^8-ep^9)*topo(1)
Coefficient rings
The coefficient ring can also be changed using the set_coefficient_ring
function which takes the variables that should be considered part of the coefficient as an argument. It will then rewrite the expression.
from symbolica import Expression
= Expression.symbol('x', 'y', 'z')
x, y, z = x*z+x*(y+2)**-1*(y+z+1)
e print(e.set_coefficient_ring([y, z]))
use std::sync::Arc;
use symbolica::atom::Atom;
fn main() {
let expr = Atom::parse("x*z+x*(y+2)^-1*(y+z+1)").unwrap();
println!("> In: {}", expr);
let expr_yz = expr.set_coefficient_ring(
&Arc::new(vec![
.get_symbol("y").into(),
state.get_symbol("z").into(),
state,
]);
)println!("> Coefficient ring y,z: {}", expr_yz);
}
which yields
[(1+3*z+y+y*z)/(2+y)]*x