GSoC 2020  Final Report
29 Aug 2020This report summarises the work I have done during GSoC 2020 for SymPy. The links to the PRs are in chronological order. For following the progress made during GSoC, see my blog for the weekly posts.
About Me
I am Sachin Agarwal, a thirdyear undergraduate student majoring in Computer Science and Engineering from the Indian Institute of Information Technology, Guwahati.
Project Synopsis
My project involved fixing and amending functions related to series expansions and limit evaluation. For further information, my proposal for the project can be referred.
Pull Requests
This section describes the actual work done during the coding period in terms of merged PRs.
Phase 1

#19292 : This PR added a condition to
limitinf
method ofgruntz.py
resolving incorrect limit evaluations. 
#19297 : This PR replaced
xreplace()
withsubs()
inrewrite
method ofgruntz.py
resolving incorrect limit evaluations. 
#19369 : This PR fixed
_eval_nseries
method ofmul.py
. 
#19432 : This PR added a functionality to the
doit
method oflimits.py
which usesis_meromorphic
for limit evaluations. 
#19461 : This PR corrected the
_eval_as_leading_term
method oftan
andsec
functions. 
#19508 : This PR fixed
_eval_nseries
method ofpower.py
. 
#19515 : This PR added
_eval_rewrite_as_factorial
and_eval_rewrite_as_gamma
methods toclass subfactorial
.
Phase 2

#19555 : This PR added
cdir
parameter to handleseries expansions
onbranch cuts
. 
#19646 : This PR rectified the
mrv
method ofgruntz.py
andcancel
method ofpolytools.py
resolvingRecursionError
andTimeout
in limit evaluations. 
#19680 : This PR added
is_Pow
heuristic tolimits.py
to improve the limit evaluations ofPow objects
. 
#19697 : This PR rectified
_eval_rewrite_as_tractable
method ofclass erf
. 
#19716 : This PR added
_singularities
toLambertW
function. 
#18696 : This PR fixed errors in assumptions when rewriting
RisingFactorial
/FallingFactorial
asgamma
orfactorial
.
Phase 3

#19741 : This PR reduced
symbolic multiples of pi
intrigonometric functions
. 
#19916 : This PR added
_eval_nseries
method tosin
andcos
. 
#19963 : This PR added
_eval_is_meromorphic
method to theclass BesselBase
. 
#18656 : This PR added
Raabe's Test
to theconcrete
module. 
#19990 : This PR added
_eval_is_meromorphic
and_eval_aseries
methods toclass lowergamma
,_eval_is_meromorphic
and_eval_rewrite_as_tractable
methods toclass uppergamma
and rectified theeval
method ofclass besselk
. 
#20002 : This PR fixed
_eval_nseries
method oflog
.
Miscellaneous Work
This section contains some of my PRs related to miscellaneous issues.

#19447 : This PR added some required testcases to
test_limits.py
. 
#19537 : This PR fixed a minor
performance
issue. 
#19604 : This PR fixed
AttributeError
in limit evaluation.
Reviewed Work
This section contains some of the PRs which were reviewed by me.
Issues Opened
This section contains some of the issues which were opened by me.
 #19670 :
Poly(E**100000000)
is slow to create.  #19752 :
gammasimp
can be improved forinteger
variables.
Examples
This section describes the bugs fixed and the new features added during GSoC.
Fixed Limit Evaluations
>>> from sympy import limit, limit_seq
>>> n = Symbol('n', positive=True, integer=True)
>>> limit(factorial(n + 1)**(1/(n + 1))  factorial(n)**(1/n), n, oo)
exp(1) # Previously produced 0
>>> n = Symbol('n', positive=True, integer=True)
>>> limit(factorial(n)/sqrt(n)*(exp(1)/n)**n, n, oo)
sqrt(2)*sqrt(pi) # Previously produced 0
>>> n = Symbol('n', positive=True, integer=True)
>>> limit(n/(factorial(n)**(1/n)), n, oo)
exp(1) # Previously produced oo
>>> limit(log(exp(3*x) + x)/log(exp(x) + x**100), x, oo)
3 # Previously produced 9
>>> limit((2*exp(3*x)/(exp(2*x) + 1))**(1/x), x, oo)
exp(1) # Previously produced exp(7/3)
>>> limit(sin(x)**15, x, 0, '')
0 # Previously it hanged
>>> limit(1/x, x, 0, dir="+")
zoo # Previously raised ValueError
>>> limit(gamma(x)/(gamma(x  1)*gamma(x + 2)), x, 0)
1 # Previously it was returned unevaluated
>>> e = (x/2) * (2*x**3  2*(x**3  1) * x**2 * digamma(x**3 + 1) + 2*(x**3  1) * x**2 * digamma(x**3 + x + 1) + x + 3)
>>> limit(e, x, oo)
1/3 # Previously produced 5/6
>>> a, b, c, x = symbols('a b c x', positive=True)
>>> limit((a + 1)*x  sqrt((a + 1)**2*x**2 + b*x + c), x, oo)
b/(2*a + 2) # Previously produced nan
>>> limit_seq(subfactorial(n)/factorial(n), n)
1/e # Previously produced 0
>>> limit(x**(2**x*3**(x)), x, oo)
1 # Previously raised AttributeError
>>> limit(n**(Rational(1, 1e9)  1), n, oo)
0 # Previously it hanged
>>> limit((1/(log(x)**log(x)))**(1/x), x, oo)
1 # Previously raised RecursionError
>>> e = (2**x*(2 + 2**(x)*(2*2**x + x + 2))/(x + 1))**(x + 1)
>>> limit(e, x, oo)
exp(1) # Previously raised RecursionError
>>> e = (log(x, 2)**7 + 10*x*factorial(x) + 5**x) / (factorial(x + 1) + 3*factorial(x) + 10**x)
>>> limit(e, x, oo)
10 # Previously raised RecursionError
>>> limit((x**2000  (x + 1)**2000) / x**1999, x, oo)
2000 # Previously it hanged
>>> limit(((x**(x + 1) + (x + 1)**x) / x**(x + 1))**x, x, oo)
exp(exp(1)) # Previously raised RecursionError
>>> limit(Abs(log(x)/x**3), x, oo)
0 # Previously it was returned unevaluted
>>> limit(x*(Abs(log(x)/x**3)/Abs(log(x + 1)/(x + 1)**3)  1), x, oo)
3 # Previously raised RecursionError
>>> limit((1  S(1)/2*x)**(3*x), x, oo)
zoo # Previously produced 0
>>> d, t = symbols('d t', positive=True)
>>> limit(erf(1  t/d), t, oo)
1 # Previously produced 1
>>> s, x = symbols('s x', real=True)
>>> limit(erf(s*x)/erf(s), s, 0)
x # Previously produced 1
>>> limit(erfc(log(1/x)), x, oo)
2 # Previously produced 0
>>> limit(erf(sqrt(x)x), x, oo)
1 # Previously produced 1
>>> a, b = symbols('a b', positive=True)
>>> limit(LambertW(a), a, b)
LambertW(b) # Previously produced b
>>> limit(uppergamma(n, 1) / gamma(n), n, oo)
1 # Previously produced 0
>>> limit(besselk(0, x), x, oo)
0 # Previously produced besselk(0, oo)
Rewrote Mul._eval_nseries()
>>> e = (exp(x)  1)/x
>>> e.nseries(x, 0, 3)
1 + x/2 + x**2/6 + O(x**3) # Previously produced 1 + x/2 + O(x**2, x)
>>> e = (2/x + 3/x**2)/(1/x + 1/x**2)
>>> e.nseries(x, n=3)
3  x + x**2 + O(x**3) # Previously produced 3 + O(x)
Rewrote Pow._eval_nseries()
>>> e = (x**2 + x + 1) / (x**3 + x**2)
>>> series(e, x, oo)
x**(5)  1/x**4 + x**(3) + 1/x + O(x**(6), (x, oo))
# Previously produced x**(5) + x**(3) + 1/x + O(x**(6), (x, oo))
>>> e = (1  1/(x/2  1/(2*x))**4)**(S(1)/8)
>>> e.series(x, 0, n=17)
1  2*x**4  8*x**6  34*x**8  152*x**10  714*x**12  3472*x**14  17318*x**16 + O(x**17)
# Previously produced 1  2*x**4  8*x**6  34*x**8  24*x**10 + 118*x**12  672*x**14  686*x**16 + O(x**17)
Added Series Expansions and Limit evaluations on BranchCuts
>>> asin(I*x + I*x**3 + 2)._eval_nseries(x, 3, None, 1)
asin(2) + pi  sqrt(3)*x/3 + sqrt(3)*I*x**2/9 + O(x**3)
>>> limit(log(I*x  1), x, 0, '')
I*pi
Rectified ff._eval_rewrite_as_gamma() and rf._eval_rewrite_as_gamma()
>>> n = symbols('n', integer=True)
>>> combsimp(RisingFactorial(10, n))
3628800*(1)**n/factorial(10  n) # Previously produced 0
>>> ff(5, y).rewrite(gamma)
120/Gamma(6  y) # Previously produced 0
Added Raabe’s Test
>>> Sum(factorial(n)/factorial(n + 2), (n, 1, oo)).is_convergent()
True # Previously raised NotImplementedError
>>> Sum((n + (n**3 + 1)**(S(1)/3))/log(n), (n, 1, oo)).is_convergent()
True # Previously raised NotImplementedError
Rewrote log._eval_nseries()
>>> f = log(x/(1  x))
>>> f.series(x, 0.491, n=1).removeO()
0.0360038887560022 # Previously raised ValueError
Future Work
 Refactoring high level functions like
series
,nseries
,lseries
andaseries
.  Add
_eval_is_meromorphic
method or_singularities
to as many special functions as possible.  Work can be done to resolve the issues opened by me (listed above).
Conclusion
This summer has been a great learning experience and has helped me get a good exposure of testdriven development. I plan to continue contributing to SymPy and will also try to help the new contributors. I am grateful to my mentors, Kalevi Suominen and Sartaj Singh for reviewing my work, giving me valuable suggestions, and being readily available for discussions.