Description
Hello,
I was wondering about the difference in standards with outputs in SymEngine and SymPy when it comes to evaluating gamma functions and their derivatives. In particular, I am interested in looking at the harmonic form of the gamma/polygamma functions, which I can access in SymPy via e.g. the [.rewrite(sp.harmonic)](https://docs.sympy.org/latest/modules/functions/special.html#sympy.functions.special.gamma_functions.polygamma)
function.
I note that I used to need to do this in SymPy version=1.11.1, though it seems to be automatic in the current version 1.13.3. As a minimal example, I am interested in the following:
import sympy as sp
n = sp.symbols('n')
f = sp.gamma(5*n+1)/sp.gamma(n+1)**5
df = sp.diff(f,n)
ddf = sp.diff(df,n)
dddf = sp.diff(ddf,n)
print([sp.expand(dddf.subs({n:x})) for x in range(5)])
In particular, in the current version of SymPy (1.13.3), I do not need to call sp.expand(dddf.subs({n:x}).rewrite(sp.harmonic))
. As an aside, as I am looking at SymEngine for optimization purposes, I was curious to see that when calling this style of computation repeatedly many times, it was faster to run in SymPy 1.11.1 and manually call sp.expand(dddf.subs({n:x}).rewrite(sp.harmonic))
as opposed to work in SymPy 1.13.3 and call sp.expand(dddf.subs({n:x}))
. I found this to be a surprise.
This means that in the current version of SymPy, running:
sp.expand(dddf.subs({n:1}))
outputs
-28800*zeta(3) - 6900 + 7700*pi**2
In the old version (1.11.1), the same code outputs:
-1485715/36 + 7700*pi**2 - 600*polygamma(2, 2) + 15000*polygamma(2, 6)
and it was faster for me to manipulate this expression by directly calling .rewrite(sp.harmonic)
.
This code can be mapped directly to SymEngine, where in the first code block we simply replace each instance of sp.foo()
with se.foo()
. I was surprised to note that calling the same command
se.expand(dddf.subs({n:1}))
gave the old version (SymPy 1.11.1) of the output
-1485715/36 + 7700*pi**2 - 600*polygamma(2, 2) + 15000*polygamma(2, 6)
and I was wondering how I should go about manipulating this in SymEngine to find the harmonic form, as I did not directly see a .rewrite()
function in the API.
I'll now say that I am interested in this harmonic representation of this expression as I am interested in the rational component of it. We see above that if I don't simplify the polygamma
s, we would naively say that the rational coefficient is 1485715/36
, whereas the number I am interested in is the - 6900
as seen above in the SymPy 13.3.3 evaluation.
I have found a way to access this by using the `.args_as_sympy()' function, and this successfully gave me each argument in terms of the updated version of SymPy where it automatically outputs values in their harmonic representation. Namely, running this on the expression above, we find:
[-1485715/36, -1200 + 1200*zeta(3), 7700*pi**2, 1280515/36 - 30000*zeta(3)]
where it seems the order of the expressions was not preserved. (The order is not important to me). With this, I can then find the rational component of each expression, finding the desired result of:
-1485715/36 -1200 + 0 + 1280515/36 = -6900
My questions are threefold:
- Is there a more natural way to find the harmonic form of the expression directly in SymEngine so I avoid passing to SymPy? I am hopeful that such an implementation will still retain the ~3x advantage in speed that SymPy 1.11.1 had when compared against running the same specific example in SymPy 1.13.3. I do not know how the relative timings change when I push the computation further.
- As I am calling
.subs()
on a given expression for many different values, is there any simplification I should be making on the expression itself? In SymPy 1.11.1 I was able to directly rewrite the expression usingrewrite(sp.harmonic)
before evaluation, rather than calling it in every individual instance of evaluation.- I did need to be careful to only do this after the derivatives were taken of the gamma functions themselves; otherwise, if I e.g. rewrote
f_harm = expr.rewrite(sp.harmonic)
and took derivatives off_harm
, evaluation would be left in terms of unevaluated derivatives ofharmonic
, e.g.\frac{d}{d \xi_{1}} \operatorname{harmonic}{\left(\xi_{1} \right)} \right|_{\substack{ \xi_{1}=5 }}
. This is no longer a problem in SymPy 1.13.3, but the code appeared to be slower. The speed results quoted above were without rewriting the expressions themselves, and rather calling either.subs()
in 1.13.3 or.subs().rewrite(sp.harmonic)
in 1.11.1.
- I did need to be careful to only do this after the derivatives were taken of the gamma functions themselves; otherwise, if I e.g. rewrote
- Is there a best practice around finding the rational value of an expression? I was naively taking the expression, and initially checking if (a) the entire expression is rational or not, and if not, (b) checking if the expression is of type
Add
. If the expression is of typeAdd
, I went through each argument of the expression running the.is_rational
check and took the sum of these to be my result.- Therefore, explicitly in the above,
-1200 + 1200*zeta(3)
is ofAdd
type and so had anis_rational
call on both-1200
and1200*zeta(3)
, and returned-1200
. I'm being sloppy with whether these are SymPy or SymEngine expressions/functions being called, but this doesn't affect the story. (I'll note that for a different project, I was surprised to find that if I declared a variable to be rational, that without evaluating the variable, the .is_rational command returned True).
- Therefore, explicitly in the above,
I'll also note that I haven't actually tried running the analogous code in SymEngine as of yet, and so I can't speak as to what the speedups are. I'm currently rewriting my code to be compatible with both the newer version of SymPy as well as SymEngine. Edit: see the additional comment below. SymEngine seems to be faster than SymPy 1.13.3 in its evaluations, but significantly slower than 1.11.1, which makes me worried that I am not using SymEngine/SymPy as intended in this new version....
Thank you for all the advice! I hope everything is properly documented and the questions are clear and well formulated, but if not, please ask for clarification!