Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast series method for ring series #9775

Merged
merged 14 commits into from
Aug 19, 2015
Merged

Conversation

shivamvats
Copy link
Member

This PR implements a series_fast method that takes an arbitrary Basic expression and expands it using sparse ring manipulations. We tried using EX domain for all expansion in #9614, but that proved to be too slow for our liking. This PR starts expanding over the QQ domain and keeps adding generators as and when required. Thus, we always work on the simplest (almost) possible domain, resulting in considerable speed-up.

The timings are extremely encouraging:

In [31]: %timeit series_fast(sin(a**2) + cos(a)*sin(a),a,100)
1 loops, best of 3: 303 ms per loop

In [32]: %timeit (sin(a**2) + cos(a)*sin(a)).series(a,0,100)
1 loops, best of 3: 5.42 s per loop

Because of the way we add generators, there is a constant overhead in expanding a given expression(irrespective of the asked order), depending on the complexity of the expression. So, for small orders, series_fast (yes, we need a new name!) might seem slow, but soon far outperforms series for even medium sized orders:

In [34]: %timeit series_fast(p, a, 10)
1 loops, best of 3: 764 ms per loop

In [35]: %timeit p.series(a,0,10)
1 loops, best of 3: 561 ms per loop

Here series_fast is actually slower than series for 10 terms, but once we expand till the 100th order:

In [36]: %timeit series_fast(p, a, 100)
1 loops, best of 3: 917 ms per loop

In [37]: %timeit p.series(a,0,100)
1 loops, best of 3: 6.67 s per loop

Clearly, the performance advantage will grow larger with larger orders.

There is a lot of scope for optimising the code (my priority was to make it work). It also needs to be played with to find bugs. However, I feel this is the way to go.

cc @certik @pernici @jksuom @thilinarmtb

ToDo
Fix wrong expansion of

  • sin(a**2 - a + 2)**5*cos(a**3 - a/2)
  • sin(x + a + b)
  • Rational exponent in argument: sin(x**QQ(1,2)). _parallel_dict_from_expr_if_gens etc need to be modified

@certik
Copy link
Member

certik commented Aug 3, 2015

Great job! How does this compare to @pernici's fastest code? Per his recent comment, it looks like this might be faster? I thought @pernici's code was performing well, but that was only for cases where general coefficients (EX) were not needed?

@shivamvats
Copy link
Member Author

I benchmarked some expressions here. The benchmarked expressions are mostly random. Is there a standard set of series for proper benchmarking?

series_fast is faster than or as fast as taylor in most cases and slower in some. Though I don't fully understand how taylor works, my guess is that taylor is much slower than series_fast when it uses EX. As long as taylor uses QQ, there can't be more than a constant difference in performance as series_fast uses QQ at all times (i.e, the backend is the same).

In cases in which series_fast is slower than taylor by more than 100%, it seems to be because series_fast had to re-expand as it did not get the required precision the first time. We can use the methods used in taylor to predict the precision functions need to be expanded upto.

@certik
Copy link
Member

certik commented Aug 4, 2015

Can you point me to which taylor you were using?

@certik
Copy link
Member

certik commented Aug 4, 2015

Also, some tests fail.

@shivamvats
Copy link
Member Author

This one

I had to change from sympy.polys.monomialtools to from sympy.polys.orderings at line 10 in rs_taylor.py to make it work.

@certik
Copy link
Member

certik commented Aug 4, 2015

I see. The benchmarks are good enough. I am fine with this. Let's finish this up and get it in.

1. rs_min_pow finds out the index of the required gen
2 Disable expand so that exp(a+b) is not expanded to exp(a)*exp(b)
@shivamvats
Copy link
Member Author

@certik @pernici The series function now works for taylor series of sin, cos, exp and tan. I have fixed the known bugs. I will gradually add other functions.

For puiseux series, we need to modify polys (as I explained in the email). Other than that this is ready for review.

def test_rs_series():
x, a, b, c = symbols('x, a, b, c')

assert (rs_series(a, a, 5)).as_expr() == a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do not need a parenthesis around rs_series:

rs_series(a, a, 5).as_expr()

else:
raise NotImplementedError

def rs_series(expr, a, prec):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a higher level function, it needs a docstring with documentation and a few doctests showing how to use it.

@certik
Copy link
Member

certik commented Aug 12, 2015

I left some comments. In general this looks good, let's fix the issues I pointed out and merge.

@shivamvats
Copy link
Member Author

I've made the changes.

@shivamvats
Copy link
Member Author

@certik Anything else to be added here?

@certik
Copy link
Member

certik commented Aug 19, 2015

I think it looks good, thanks!

certik added a commit that referenced this pull request Aug 19, 2015
Fast `series` method for ring series
@certik certik merged commit a2f490f into sympy:master Aug 19, 2015
@shivamvats
Copy link
Member Author

Thanks for the review!

@shivamvats shivamvats deleted the ring_series_fast branch August 19, 2015 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants