Sachin Agarwal Open Source Enthusiast

GSoC 2020 - Final Report

This 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 third-year 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 of gruntz.py resolving incorrect limit evaluations.

  • #19297 : This PR replaced xreplace() with subs() in rewrite method of gruntz.py resolving incorrect limit evaluations.

  • #19369 : This PR fixed _eval_nseries method of mul.py.

  • #19432 : This PR added a functionality to the doit method of limits.py which uses is_meromorphic for limit evaluations.

  • #19461 : This PR corrected the _eval_as_leading_term method of tan and sec functions.

  • #19508 : This PR fixed _eval_nseries method of power.py.

  • #19515 : This PR added _eval_rewrite_as_factorial and _eval_rewrite_as_gamma methods to class subfactorial.

Phase 2

  • #19555 : This PR added cdir parameter to handle series expansions on branch cuts.

  • #19646 : This PR rectified the mrv method of gruntz.py and cancel method of polytools.py resolving RecursionError and Timeout in limit evaluations.

  • #19680 : This PR added is_Pow heuristic to limits.py to improve the limit evaluations of Pow objects.

  • #19697 : This PR rectified _eval_rewrite_as_tractable method of class erf.

  • #19716 : This PR added _singularities to LambertW function.

  • #18696 : This PR fixed errors in assumptions when rewriting RisingFactorial / FallingFactorial as gamma or factorial.

Phase 3

  • #19741 : This PR reduced symbolic multiples of pi in trigonometric functions.

  • #19916 : This PR added _eval_nseries method to sin and cos.

  • #19963 : This PR added _eval_is_meromorphic method to the class BesselBase.

  • #18656 : This PR added Raabe's Test to the concrete module.

  • #19990 : This PR added _eval_is_meromorphic and _eval_aseries methods to class lowergamma, _eval_is_meromorphic and _eval_rewrite_as_tractable methods to class uppergamma and rectified the eval method of class besselk.

  • #20002 : This PR fixed _eval_nseries method of log.

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 for integer 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 Branch-Cuts

>>> 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 and aseries.
  • 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 test-driven 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.

GSoC 2020 - Week 12

Key highlights of this week’s work are:

  • Fixed _eval_nseries() of log

    • This was a long pending issue. It was necessary to refactor the _eval_nseries method of log to fix some minor issues.

This marks the end of Phase-3 of the program. Finally, this brings us to the end of Google Summer of Code and I am really thankful to the SymPy Community and my mentor Kalevi Suominen for always helping and supporting me.

GSoC 2020 - Week 11

Key highlights of this week’s work are:

  • Implemented Raabe’s Test

    • This was a long pending issue. Raabe's Test helps to determine the convergence of a series. It has been added to the concrete module to handle those cases when the ratio test becomes inconclusive.

  • Fixed limit evaluations related to lowergamma, uppergamma and besselk function

    • We added _eval_is_meromorphic method to class lowergamma and uppergamma so that some of the limits involving lowergamma and uppergamma functions get evaluated using the meromorphic check already present in the limit codebase. Now, to make lowergamma and uppergamma functions tractable for limit evaluations, _eval_aseries method was added to lowergamma and _eval_rewrite_as_tractable to uppergamma.

      Finally, we also rectified the eval method of class besselk, so that besselk(nu, oo) automatically evaluates to 0.

GSoC 2020 - Week 10

Key highlights of this week’s work are:

  • Added _eval_is_meromorphic() to bessel function

    • We added the _eval_is_meromorphic method to the class BesselBase so that some of the limits involving bessel functions get evaluated using the meromorphic check already present in the limit codebase.

GSoC 2020 - Week 9

Key highlights of this week’s work are:

  • Fixed periodicity of trigonometric function

    • This was a long pending issue. The _peeloff_pi method of sin had to be rectified to reduce the symbolic multiples of pi in trigonometric functions. As a result of this fix, now sin(2*n*pi + 4) automatically evaluates to sin(4), when n is an integer.

  • Fixed limit evaluations related to trigonometric functions

    • In this PR, we added _eval_nseries method to both sin and cos to make the limit evaluations more robust when it comes to trigonometric functions. We also added a piece of code to cos.eval method so that the limit of cos(m*x), where m is non-zero, and x tends to oo evaluates to AccumBounds(-1, 1).