All test produced relative to the latest smichr/2084 branch, because
master branch has a huge of errors. I have not tested very complex
cases, because in simple cases issuses were rised.
Remarks that I have made are only my opinion, may be in aggressive manner.
And I have no finish conclusion.
I think that Aaron will dispatch it after discussions.
The reStructeredText version is an atachment. HTML version of this
http://www.goodok.ru/sympy/series/sympy-series.html
Thanks.
---------------------
smichr/2084 IPython console for SymPy 0.6.7-git (Python 2.6.6)
|date| |time|.
.. contents:: Table of Contents
===============
Tests
===============
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = map(Function, 'fgh')
Sources of examples for test work-flow
========================================
* simple functions
* http://www.maplesoft.com/support/help/Maple/view.aspx?path=series
* http://reference.wolfram.com/mathematica/ref/Series.html
Types of remarks
================
_`The Big-O is not present at point==0`.
--------------------------------------------
The Big-O was expected, but there is no one. And also series are
not truncated.
(This type of remark must be distinguished from `The Big-O is not
present at point != 0`_ case, where there is not present one because of
some reasons.)
The typical examples are:
>>> ((1+x)**7).series(x, n=3)
1 + 7*x + 21*x**2 + 35*x**3 + 35*x**4 + 21*x**5 + 7*x**6 + x**7
but expected:
1 + 10*x + 45*x**2 + O(x**3)
>>> abs(x + x**2).series(n=1)
x + x**2
but expected:
O(x)
Note that in some cases Big-O is not present correctly:
>>> ((1+x)**2).series(x, n=6)
1 + 2*x + x**2
>>> (1 + 1/x).series()
1 + 1/x
Ok
But even in this case this collaborate with the next one: `Method
"lseries" yield many terms`_.
_`Method "lseries" yield many terms`
-------------------------------------
This type of errors possibly arise because of `The Big-O is not
present at point==0`_ error.
The typical examples are:
>>> (x+1/x).lseries().next()
x + 1/x
expected: 1/x
>>> (x+x**2).lseries().next()
x + x**2
expected: x
>>> ((1+x)**7).lseries(x).next()
1 + 7*x + 21*x**2 + 35*x**3 + 35*x**4 + 21*x**5 + 7*x**6 + x**7
expected: 1
_`Incorrect power in Big-O expression`
--------------------------------------------
Typical examples are:
>>> (exp(x)/x**2).series()
1/2 + x/6 + 1/x + x**(-2) + x**2/24 + x**3/120 + O(x**4)
>>> (exp(x)*x**2).series()
x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
>>> (exp(x)/x**4).series()
1/24 + x/120 + 1/(6*x) + x**(-4) + x**(-3) + 1/(2*x**2) + O(x**2)
>>> (sin(x)/x**4).series()
x/120 - 1/(6*x) + x**(-3) + O(x**2)
compare
>>> (exp(x)*x**2).series()
x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
with
>>> ((exp(x)).series() * x**2).expand()
x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
_`The Big-O is not present at point != 0`.
--------------------------------------------
It was discussed.
It is not implemented because of some reasons: it was deviation
from the main issues and there is no understanding what to implement
exactly and how.
_`"lseries" with two-variable function`
----------------------------------------
Typical examples are:
>>> sin(x+y).series(x, n=3)
x*cos(y) - x**2*sin(y)/2 + sin(y) + O(x**3)
>>> (sin(x+y)).series(x, n=3).series(y, n=3)
x + y - x*y**2/2 - y*x**2/2 + O(x**3) + O(y**3)
Ok, but
>>> g = (sin(x+y)).series(x, n=3).lseries(y)
>>> g.next()
O(x**3)
is incorrect, and
>>> # g.next()
>>> # StopIteration:
rise exception
_`Sort order of the terms is not canonical`
---------------------------------------------
This is because of automatic "sympy idiosyncracies".
It is not a really big error. But it confuse the result.
>>> exp(x).series(x,1,n=3)
E - E*(1 - x) + E*(1 - x)**2/2
Ok
>>> (x**2 + x+1/x).series()
x + 1/x + x**2
>>> 1/x + x + x**2
x + 1/x + x**2
Expected: 1/x + x + x**2
>>> cos(x).series(x,1,n=2)
(1 - x)*sin(1) + cos(1)
>>> cos(1) - sin(1)*(x-1)
(1 - x)*sin(1) + cos(1)
Expected: cos(1) - sin(1)*(x-1) + O((x-1)**2)
At the same time the 'lseries' method work more better:
>>> g = cos(x).lseries(x,1)
>>> g.next()
cos(1)
>>> g.next()
(1 - x)*sin(1)
Though more canonical result for series term could be "-(x - 1)*sin(1)"
_`Bases functions are not the powers of natural`
------------------------------------------------
It is not a error.
>>> (exp(x)*log(x)).series(n=3)
x*log(x) + x**2*log(x)/2 + log(x) + O(x**3*log(x))
>>> (sqrt(x)*exp(x)).series(n=3)
x**(1/2) + x**(3/2) + x**(5/2)/2 + O(x**(7/2))
>>> (sqrt(sin(x))).series()
x**(1/2) - x**(5/2)/12 + x**(9/2)/1440 + O(x**6)
>>> (sin(sqrt(x))).series()
x**(1/2) - x**(3/2)/6 + x**(5/2)/120 - x**(7/2)/5040 + O(x**6)
>>> (log(sin(x))).series()
log(x) - x**2/6 - x**4/180 + O(x**6)
But we must note that those cases are not real "natural power series".
_`Insufficient of operations with series`
------------------------------------------
It is for a future.
Some operation are work fine due to the fine "Order" class realization:
#) multiplication "series" by "expr"
>>> exp(x).series()
1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120 + O(x**6)
>>> (exp(x).series() * x).expand()
x + x**2 + x**3/2 + x**4/6 + x**5/24 + x**6/120 + O(x**7)
Ok. We assume that x ==> 0. So multiplication x by O(x**6)
yield O(x**7).
>>> (exp(x).series()*y).expand()
y + x*y + y*x**2/2 + y*x**3/6 + y*x**4/24 + y*x**5/120 + O(x**6)
Ok, y is not present in series arguments, so it is considered
as constant for O(x**6)
>>> (exp(x).series() * (x +1)).expand()
1 + 2*x + 3*x**2/2 + 2*x**3/3 + 5*x**4/24 + x**5/20 + O(x**6)
Ok. O(x**6) "eat" O(x**7) correctly.
#) Multiplication series by series.
>>> (exp(x).series() * exp(-x).series()).expand()
1 + O(x**6)
Ok. O(x**6) "eat" higher powers.
>>> (exp(x).series(n=3) * exp(-x).series()).expand()
1 + O(x**3)
Ok. O(x**3) "eat" higher powers and O(x**6) too.
But Operations at point != 0 are not work fine:
>>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1, n=4)).expand()
8/9 + x/2 - 11*x**2/12 + 8*x**3/9 - x**4/2 + x**5/6 - x**6/36
Expected "1 + O((x-1)**4)"
Compare with:
>>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1,
n=4)).subs(x, x+1).expand()
1 - x**4/12 - x**6/36
This is because of `The Big-O is not present at point != 0`_ .
My notes: the realization of summation and multiplication of
'series' with each other could be connected with `Sort order of the
terms is not canonical`_ . It is clear that if I know terms then I know
how to produce the result. (Though I don't clear understand what to do
with multiplication "series" by "expr" )
#) generator of terms.
In core there are three methods "series" - main, "nseries",
"lseries"
"lseries" used for yielding in some core spheres. (Though I
donn't clear understand the name-token.)
As I understand, "nseries" is a master for "lseries" by default
for base Expr class, so that is not effectively. It is mentioned in code
comments.
It is became historically. But also mentioned that subclasses
must be implement "lseries" as needed in a better way.
In my opinion "lseries" must be a master for "nseries" in
future. At least for "natural power series". Though in some cases (when
series are not "natural power series") it can be out of sense and in any
case it is a deal for the internal realization after examination all of
risks.
#) generator for coefficients.
It differ from "lseries" so that only coefficients yields,
without "x**i" multiplicators.
It is concerned only the pure "natural powers series" (or other
formalized series, may by for "log(x)*x**i" terms and even for Laurent
"1/x + 1 + x" too)
In some cases the method for coefficients could be needed. For
the internal realization it could be convenient too.
Do generators must skip 0 terms on some orders it is another
question for mind.
sin(x) ==> [0, 1, 0, -1/6, 0, ...]
#) Suggested operations:
InverseSeries - does series reversion to find the series for
the inverse function of a series:
(From wolfram mathematica)
some function can work with series:
>>> exp(x + O(x**3))
exp(x + O(x**3))
I expected the proceeding "exp" as series : "1 + (x + O(x**3))
+ (x + O(x**3))**2/2 + (x + O(x**3))**3/6 + ..."
so it resulting to "1 + x + x**2/2 + O(x**3)"
Though it lucky produced by this way "manually":
>>> exp(x + O(x**3)).expand()
exp(x)*exp(O(x**3))
>>> ( exp(x).series() * exp(O(x**3)).series() ).expand()
1 + x + x**2/2 + O(x**3)
But will be better to implement it in automatic mode.
_`Realization for abstract analytic function`
----------------------------------------------
Series of derivations
>>> D = Derivative
>>> assert D(x**2 + x**3*y**2, x, 2, y, 1).series(x).doit() == 12*x*y
>>> assert D(cos(x), x).lseries().next() == D(1, x)
>>> assert D(exp(x), x).series(n=3) == D(1, x) + D(x, x) + O(x**2)
>>> assert Integral(x, (x, 1, 3),(y, 1, x)).series(x) == -4 + 4*x
Ok
Chris successfully have done those realizations, I took them from
the latest smichr/2084 branch.
For abstract analytic function I mean that we do "series" on it.
Take some Function "f" and imply series method for it:
>>> # series(f(x), x)
>>> # rises RuntimeError: maximum recursion depth exceeded while
calling a Python object
Expected something like: f(0) + D(f(0), x).subs(x, 0)*x + ...
BTW, I donn't know how does the substitution for derivatives work.
>>> D(f(x), x).subs(x, 0)
f(0)
I expected something like "f_x(0)"
Now play with derivation of series of exp(x):
>>> D(exp(x), x).series()
D(1, x) + D(x, x) + D(x**2/2, x) + D(x**3/6, x) + D(x**4/24, x) +
O(x**5)
>>> D(exp(x).series(), x).doit()
1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
It work, but "D.doit()" do not work with Big-O.
_`Multiple cases results must to be more better`
------------------------------------------------
(Piecewise functions ?)
In some cases abstract series can produce a set of the results.
For example transpose "abs(x)" to the right by "c" and look what is
in the zero point.
>>> c = var("c")
>>> abs(x-c).series(x)
-(x - c)*sign(c)
Expected something like: "[-(x - c)*sign(c); when x!=0], [-x; when
c==0]"
It is difficult, in some cases solution of equations is needed.
_`Series with assumption x limit to oo`
----------------------------------------
(raw issue)
>>> # (sin(1/x)).series(x, oo, n=5)
>>> # or
>>> # (sin(1/x)).series(x, oo, n=-5)
# rise Exception
Expected: 1/x + (1/x)**3)/6 + O((1/x)**5)
_`Series with matrices`
------------------------
>>> m = Matrix(2, 2, [0,1,1,0])
>>> # exp(m)
>>> # SympifyError: SympifyError: 'Matrix cannot be sympified'
>>> # exp(m*x)
>>> # SympifyError: SympifyError: 'Matrix cannot be sympified'
Unfortunately exponent of matrices is not present in sympy.
It would be good to realize this. May be with the aim of series.
>
> I have tested base series work-flow (which was interesting to me) and present result.
>
> All test produced relative to the latest smichr/2084 branch, because master branch has a huge of errors. I have not tested very complex cases, because in simple cases issuses were rised.
>
> Remarks that I have made are only my opinion, may be in aggressive manner.
>
> And I have no finish conclusion.
> I think that Aaron will dispatch it after discussions.
I'm sorry, but I think due to English problems, I can't understand what you mean here.
>
> The reStructeredText version is an atachment. HTML version of this
> http://www.goodok.ru/sympy/series/sympy-series.html
>
I added this to our wiki (which automatically renders the rst). See https://github.com/sympy/sympy/wiki/sympy-series. If you wish to make further changes or additions, you can make them there.
Feel free to use our wiki. As long as it is sympy related, you can use it.
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sy...@googlegroups.com.
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
>
> <sympy-series.rst>
Alexey, thanks for looking this over.
One thing that would be helpful is if we can differentiate between fixing past problems (without breaking anything) and identifying additional problems. Most of what you identified are existing problems that were not introduced by the changes under review. Nonetheless, I'll see what I can do to fix them and give comments below:
>> _`The Big-O is not present at point==0`.
>> --------------------------------------------
>>
>> >>> ((1+x)**7).series(x, n=3)
>> 1 + 7*x + 21*x**2 + 35*x**3 + 35*x**4 + 21*x**5 + 7*x**6 + x**7
>>
>> but expected:
>> 1 + 10*x + 45*x**2 + O(x**3)
If O(x**3) is simply added to the result, it will eat all the x**3 and greater terms, so I think this will be easy to fix.
But if nothing is eaten then it should be left out. Adding O(x**6) to the following doesn't remove any terms so it should not be included.
>> >>> ((1+x)**2).series(x, n=6)
>> 1 + 2*x + x**2
>> _`Method "lseries" yield many terms`
This is a known problem. Perhaps an O(x**n) could be added at each step to eat what shouldn't be there.
h[8] >>> x+1/x+O(x)
1/x + O(x)
h[9] >>> x+1/x+O(x**2)
x + 1/x + O(x**2)
>> _`Incorrect power in Big-O expression`
>> --------------------------------------------
>>
>> Typical examples are:
>>
>> >>> (exp(x)/x**2).series()
>> 1/2 + x/6 + 1/x + x**(-2) + x**2/24 + x**3/120 + O(x**4)
>> >>> (exp(x)*x**2).series()
>> x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
>> >>> (exp(x)/x**4).series()
>> 1/24 + x/120 + 1/(6*x) + x**(-4) + x**(-3) + 1/(2*x**2) + O(x**2)
>> >>> (sin(x)/x**4).series()
>> x/120 - 1/(6*x) + x**(-3) + O(x**2)
>>
>> compare
>>
>> >>> (exp(x)*x**2).series()
>> x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
>>
>> with
>>
>> >>> ((exp(x)).series() * x**2).expand()
>> x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
>>
Could you please be explicit here? I'm not sure what is expected. I can fix it if you can tell me what the answer should be.
>>
>> _`The Big-O is not present at point != 0`.
>> --------------------------------------------
>>
>> It was discussed.
>> It is not implemented because of some reasons: it was deviation
>> from the main issues and there is no understanding what to implement
>> exactly and how.
>>
yes
>> _`"lseries" with two-variable function`
>> ----------------------------------------
>>
>> Typical examples are:
>>
>> >>> sin(x+y).series(x, n=3)
>> x*cos(y) - x**2*sin(y)/2 + sin(y) + O(x**3)
>> >>> (sin(x+y)).series(x, n=3).series(y, n=3)
>> x + y - x*y**2/2 - y*x**2/2 + O(x**3) + O(y**3)
>>
>> Ok, but
>>
>> >>> g = (sin(x+y)).series(x, n=3).lseries(y)
>> >>> g.next()
>> O(x**3)
The un-removed O from the series is eating terms. Any O should be removed before computing the series I would guess.
>> _`Sort order of the terms is not canonical`
>> ---------------------------------------------
>>
>> This is because of automatic "sympy idiosyncracies".
>> It is not a really big error. But it confuse the result.
Yes, that's just part of sympy. A regular expression is returned. Perhaps some flag can be set to change this but it is not something I ever change since it doesn't matter to me.
>> _`Bases functions are not the powers of natural`
>> ------------------------------------------------
>>
>> It is not a error.
>>
>> >>> (exp(x)*log(x)).series(n=3)
>> x*log(x) + x**2*log(x)/2 + log(x) + O(x**3*log(x))
>>
>> But we must note that those cases are not real "natural power
>> series".
What is it that you would expect to see here? (This is the same sort of expresion obtained from wolfram alpha.)
Yes...since there is no O() term there is nothing to eat the other terms. I wonder if the O(x) term could be introduced to eat the excess terms but then be removed.
>>
>> >>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1,
>> n=4)).expand() 8/9 + x/2 - 11*x**2/12 + 8*x**3/9 - x**4/2 + x**5/6 - x**6/36
>>
>> Expected "1 + O((x-1)**4)"
I think this is an important point so I want to make sure I am understanding this because I'm not sure that I would expect to get a cubic from each expression that correctly matches exp(x) or exp(-x) at x=1. If I substituted x=1 into the result I would expect to see an exact match between the function and the series, and I do:
>>> S('8/9 + x/2 - 11*x**2/12 + 8*x**3/9 - x**4/2 + x**5/6 - x**6/36').subs(x,1)
1
I wouldn't expect to see only a constant 1 for the expansion. Perhaps the O(x**4) should eat all the terms except the 8*x**3/9, but since we don't retain an O() term for this case that can't happen and you will always have too many terms in the product.
>>
>> Compare with:
>>
>> >>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1, n=4)).subs(x, x+1).expand()
>> 1 - x**4/12 - x**6/36
>>
This is an entirely different thing, isn't it?. That expression represents exp(x)*exp(-x) at x=0...and plugging in x=0 must give 1 (as it does).
>> My notes: the realization of summation and multiplication of
>> 'series' with each other could be connected with `Sort order of the
>> terms is not canonical`_ . It is clear that if I know terms then I
>> know how to produce the result. (Though I don't clear understand what to
>> do with multiplication "series" by "expr" )
>>
>> #) generator of terms.
>>
>> In core there are three methods "series" - main, "nseries",
>> "lseries"
>>
>> "lseries" used for yielding in some core spheres. (Though I
>> donn't clear understand the name-token.)
It means "lazy series"...only a new term at a time is returned rather than many at once.
>>
>> As I understand, "nseries" is a master for "lseries" by
>> default for base Expr class, so that is not effectively. It is mentioned in
>> code comments. It is became historically. But also mentioned that subclasses
>> must be implement "lseries" as needed in a better way.
>>
>> In my opinion "lseries" must be a master for "nseries" in
>> future. At least for "natural power series". Though in some cases
>> (when series are not "natural power series") it can be out of sense and in
>> any case it is a deal for the internal realization after examination all
>> of risks.
Yes, lseries works too hard at present in that it keeps asking for more and more terms from nseries until it gets the next terms. This is an issue separate from what I am trying to do in these commits.
>>
>> #) generator for coefficients.
>>
>> It differ from "lseries" so that only coefficients yields,
>> without "x**i" multiplicators.
>>
>> It is concerned only the pure "natural powers series" (or
>> other
>> formalized series, may by for "log(x)*x**i" terms and even for
>> Laurent "1/x + 1 + x" too)
>>
>> In some cases the method for coefficients could be needed.
>> For the internal realization it could be convenient too.
>>
>> Do generators must skip 0 terms on some orders it is another
>> question for mind.
>> sin(x) ==> [0, 1, 0, -1/6, 0, ...]
>>
>> #) Suggested operations:
>>
>> InverseSeries - does series reversion to find the series for
>> the inverse function of a series:
>> (From wolfram mathematica)
These are beyond the scope of what I am trying to do. Perhaps I can review something that you propose?
>>
>>
>> some function can work with series:
>>
>> >>> exp(x + O(x**3))
>> exp(x + O(x**3))
>>
>> I expected the proceeding "exp" as series : "1 + (x +
>> O(x**3)) + (x + O(x**3))**2/2 + (x + O(x**3))**3/6 + ..."
>> so it resulting to "1 + x + x**2/2 + O(x**3)"
>>
>> Though it lucky produced by this way "manually":
>>
>> >>> exp(x + O(x**3)).expand()
>> exp(x)*exp(O(x**3))
>> >>> ( exp(x).series() * exp(O(x**3)).series() ).expand()
>> 1 + x + x**2/2 + O(x**3)
>>
>> But will be better to implement it in automatic mode.
>>
That's an interesting idea...but again, not within the scope of my work here.
>>
>> _`Realization for abstract analytic function`
>> ----------------------------------------------
>>
>> Series of derivations
>>
>> >>> D = Derivative
>> >>> assert D(x**2 + x**3*y**2, x, 2, y, 1).series(x).doit() ==
>> 12*x*y >>> assert D(cos(x), x).lseries().next() == D(1, x)
>> >>> assert D(exp(x), x).series(n=3) == D(1, x) + D(x, x) +
>> O(x**2) >>> assert Integral(x, (x, 1, 3),(y, 1, x)).series(x) ==
>> -4 + 4*x
>>
>> Ok
>>
>> Chris successfully have done those realizations, I took them from
>> the latest smichr/2084 branch.
>>
>> For abstract analytic function I mean that we do "series" on it.
>> Take some Function "f" and imply series method for it:
>>
>> >>> # series(f(x), x)
>> >>> # rises RuntimeError: maximum recursion depth exceeded while
>> calling a Python object
It seems like this should be fixed.
>>
>> Expected something like: f(0) + D(f(0), x).subs(x, 0)*x + ...
I'm not sure we have a way to represent D(f(x), x)|_x=0. We need something like an "unevaluated subs" to do this.
>>
>> BTW, I donn't know how does the substitution for derivatives
>> work.
>>
>> >>> D(f(x), x).subs(x, 0)
>> f(0)
This isn't right...subs shouldn't be allowed to change the differentitation variable to anything but a symbol (unless some sort of transformation is done as for Integrals):
h[33] >>> f(x).diff(x).subs(x,y+2)
D(f(2 + y), 2 + y)
h[34] >>> _.subs(f,sin)
D(sin(2 + y), 2 + y)
h[35] >>> _.doit()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sympy\core\function.py", line 617, in doit
return Derivative(expr, *self.symbols, **hints)
File "sympy\core\function.py", line 589, in __new__
raise ValueError('Invalid literal: %s is not a valid variable' % s)
ValueError: Invalid literal: 2 + y is not a valid variable
>>
>> I expected something like "f_x(0)"
>>
>> Now play with derivation of series of exp(x):
>>
>> >>> D(exp(x), x).series()
>> D(1, x) + D(x, x) + D(x**2/2, x) + D(x**3/6, x) + D(x**4/24, x) +
>> O(x**5)
>> >>> D(exp(x).series(), x).doit()
>> 1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
>>
>> It work, but "D.doit()" do not work with Big-O.
Do you know what it should be? I suspect that you just differentiate the O.expr so O(x**2) becaomes O(x). If so the change that I made gives:
h[7] >>> D(exp(x).series(), x).doit()
1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
>>
>> _`Multiple cases results must to be more better`
>> ------------------------------------------------
>>
>> (Piecewise functions ?)
>>
>> In some cases abstract series can produce a set of the results.
>> For example transpose "abs(x)" to the right by "c" and look what
>> is in the zero point.
>>
>> >>> c = var("c")
>> >>> abs(x-c).series(x)
>> -(x - c)*sign(c)
>>
>> Expected something like: "[-(x - c)*sign(c); when x!=0], [-x; when c==0]"
Yes, this should be changed.
>>
>> It is difficult, in some cases solution of equations is needed.
>>
Can you give an example?
>>
>> _`Series with assumption x limit to oo`
>> ----------------------------------------
>>
>> (raw issue)
>>
>> >>> # (sin(1/x)).series(x, oo, n=5)
>> >>> # or
>> >>> # (sin(1/x)).series(x, oo, n=-5)
>> # rise Exception
>>
>> Expected: 1/x + (1/x)**3)/6 + O((1/x)**5)
I'll look at this.
>>
>> _`Series with matrices`
>> ------------------------
>>
>> >>> m = Matrix(2, 2, [0,1,1,0])
>> >>> # exp(m)
>> >>> # SympifyError: SympifyError: 'Matrix cannot be sympified'
>> >>> # exp(m*x)
>> >>> # SympifyError: SympifyError: 'Matrix cannot be sympified'
>>
>> Unfortunately exponent of matrices is not present in sympy.
>>
>> It would be good to realize this. May be with the aim of series.
>>
Again, this is a separate issue.
I'll post something in the next day or two regarding any changes.
/c
Fixing this I think would require a somewhat major rewrite of how the printing system works. Right now, all expressions are sorted in the same way to produce the ordering of the printed result. In fact, the only reason that power series print in the correct "order" is that the default is set to reverse lexicographic order. This will hopefully change to regular lexicographic order in the next release because it makes everything else print "backwards", but then series will print "backwards".
Probably a better way would be to allow any Basic object to include a manual printing order if it so desires, which would override the default. This would allow series to print in the correct order series wide, even in more complex cases like this one, but polynomials would still print in the regular lexicographic order.
Of course, there's then the question of how it should work if those objects were later combined with other objects. Should it just clear the printing order or should it attempt to retain it, and if the latter, to what extent?
Mateusz has already started implementing this I believe. See https://github.com/mattpap/sympy-polys/commit/85e69f1395d21fb51c92ab96274ec76f5bb2ff1a. Is that the same thing?
See issue 1620 (http://code.google.com/p/sympy/issues/detail?id=1620).
>
>>>
>>> BTW, I donn't know how does the substitution for derivatives
>>> work.
>>>
>>>>>> D(f(x), x).subs(x, 0)
>>> f(0)
>
> This isn't right...subs shouldn't be allowed to change the differentitation variable to anything but a symbol (unless some sort of transformation is done as for Integrals):
>
> h[33] >>> f(x).diff(x).subs(x,y+2)
> D(f(2 + y), 2 + y)
> h[34] >>> _.subs(f,sin)
> D(sin(2 + y), 2 + y)
> h[35] >>> _.doit()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "sympy\core\function.py", line 617, in doit
> return Derivative(expr, *self.symbols, **hints)
> File "sympy\core\function.py", line 589, in __new__
> raise ValueError('Invalid literal: %s is not a valid variable' % s)
> ValueError: Invalid literal: 2 + y is not a valid variable
Ditto to above.
Aaron Meurer
--
You received this message because you are subscribed to the Google Groups "sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
h[1] >>> ((1+x)**7).series(x, n=3)
1 + 7*x + 21*x**2 + O(x**3)
>>
>> >>> abs(x + x**2).series(n=1)
>> x + x**2
>>
>> but expected:
>> O(x)
h[2] >>> abs(x + x**2).series(n=1)
O(x)
>>
>> Note that in some cases Big-O is not present correctly:
>>
>> >>> ((1+x)**2).series(x, n=6)
>> 1 + 2*x + x**2
>> >>> (1 + 1/x).series()
>> 1 + 1/x
>>
h[3] >>> abs(x + x**2).series(n=2)
x + O(x**2)
h[4] >>> ((1+x)**2).series(x, n=6)
1 + 2*x + x**2
h[5] >>> (1 + 1/x).series()
1 + 1/x
>> >>> (x+1/x).lseries().next()
>> x + 1/x
>>
>> expected: 1/x
h[6] >>> (x+1/x).lseries().next()
1/x
>>
>> >>> (x+x**2).lseries().next()
>> x + x**2
>>
>> expected: x
>>
h[7] >>> (x+x**2).lseries().next()
x
>> >>> ((1+x)**7).lseries(x).next()
>> 1 + 7*x + 21*x**2 + 35*x**3 + 35*x**4 + 21*x**5 + 7*x**6 + x**7
>>
>> expected: 1
h[8] >>> ((1+x)**7).lseries(x).next()
1
>>
>> _`Incorrect power in Big-O expression`
>> --------------------------------------------
I'm looking into this.
>> _`"lseries" with two-variable function`
>> >>> g = (sin(x+y)).series(x, n=3).lseries(y)
>> >>> g.next()
>> O(x**3)
h[9] >>> (sin(x+y)).series(x, n=3).lseries(y).next()
x
>> >>> D(exp(x).series(), x).doit()
>> 1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
h[10] >>> Derivative(exp(x).series(), x).doit()
1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
>> >>> c = var("c")
>> >>> abs(x-c).series(x)
>> -(x - c)*sign(c)
>>
h[11] >>> abs(x-c).series(x)
Piecewise((x, -c == 0), (-(x - c)*sign(c), True))
>> _`Series with assumption x limit to oo`
>> ----------------------------------------
I'm looking at this.
/c
Sorry, I'm ill at this days, so I will answer for a few days.
12.02.2011 05:27, Chris Smith пишет:
h[1] >>> f=Function('f'); f(x).series()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sympy\core\expr.py", line 852, in series
s1 = s._eval_nseries(x, n=n)
File "sympy\core\function.py", line 299, in _eval_nseries
multi-arg functions are not supported.')
NotImplementedError: series for user-defined and multi-arg functions are not sup
ported.
h[1] >>> sin(1/x).series(x,oo)
1/x + 1/(120*x**5) - 1/(6*x**3)
/c
>> >>> ((1+x)**7).series(x, n=3)
>> 1 + 7*x + 21*x**2 + 35*x**3 + 35*x**4 + 21*x**5 + 7*x**6 + x**7
>>
>> but expected:
>> 1 + 10*x + 45*x**2 + O(x**3)
h[1] >>> ((1+x)**7).series(x, n=3)
1 + 7*x + 21*x**2 + O(x**3)
>>
>> >>> abs(x + x**2).series(n=1)
>> x + x**2
>>
>> but expected:
>> O(x)
h[2] >>> abs(x + x**2).series(n=1)
O(x)
>>
>> Note that in some cases Big-O is not present correctly:
>>
>> >>> ((1+x)**2).series(x, n=6)
>> 1 + 2*x + x**2
>> >>> (1 + 1/x).series()
>> 1 + 1/x
>>
h[3] >>> abs(x + x**2).series(n=2)
x + O(x**2)
>> >>> D(exp(x).series(), x).doit()
>> 1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
h[10] >>> Derivative(exp(x).series(), x).doit()
1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
>> >>> c = var("c")
>> >>> abs(x-c).series(x)
>> -(x - c)*sign(c)
>>
h[11] >>> abs(x-c).series(x)
Piecewise((x, -c == 0), (-(x - c)*sign(c), True))
But the series is being calculated from the positive direction;
if you are leaving out the x**2 term, isn't this O(x**2)?
>> h[9] >>> (sin(x+y)).series(x, n=3).lseries(y).next()
>> x
>
> This is wrong, the O(x**3) term could have any expansion as a function
> of y.
If you expand in y the result of the first series you get:
>>> (sin(x+y)).series(x, n=3)
x*cos(y) - x**2*sin(y)/2 + sin(y) + O(x**3)
>>> _.removeO().series(y,n=3)
x + y - x*y**2/2 - y*x**2/2 + O(y**3)
It seems logical to return the first non-y dependent term, doesn't it?
Any help as to what it should be would be appreciated.
>>>> >>> D(exp(x).series(), x).doit()
>>>> 1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
>>
>> h[10] >>> Derivative(exp(x).series(), x).doit()
>> 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
>
> Wrong: the derivative of a O() term can be arbitrarily large.
Can you give any direction as to what O(x**n).diff(x) should be?
>
>>>> >>> c = var("c")
>>>> >>> abs(x-c).series(x)
>>>> -(x - c)*sign(c)
>>>>
>> h[11] >>> abs(x-c).series(x)
>> Piecewise((x, -c == 0), (-(x - c)*sign(c), True))
>>
>
> abs(x) != x so this can't be right in the case c==0.
If c is 0, abs(x-c) -> abs(x) which is x when approached from the
right.
/c
Series expansions are usually expected to be valid from both directions.
There's nothing indicating that it's not the case here.
>
> >> h[9] >>> (sin(x+y)).series(x, n=3).lseries(y).next()
> >> x
> >
> > This is wrong, the O(x**3) term could have any expansion as a function
> > of y.
>
> If you expand in y the result of the first series you get:
>
> >>> (sin(x+y)).series(x, n=3)
> x*cos(y) - x**2*sin(y)/2 + sin(y) + O(x**3)
> >>> _.removeO().series(y,n=3)
> x + y - x*y**2/2 - y*x**2/2 + O(y**3)
>
> It seems logical to return the first non-y dependent term, doesn't it?
> Any help as to what it should be would be appreciated.
You can't just remove the O(x**3) term. Doing so hides the fact that
you've already done an expansion on x and ignores the dependency of
O(x**3) on y.
>
> >>>> >>> D(exp(x).series(), x).doit()
> >>>> 1 + x + D(O(x**6), x) + x**2/2 + x**3/6 + x**4/24
> >>
> >> h[10] >>> Derivative(exp(x).series(), x).doit()
> >> 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
> >
> > Wrong: the derivative of a O() term can be arbitrarily large.
>
> Can you give any direction as to what O(x**n).diff(x) should be?
Basically, you can't say anything about O(x**n).diff(x). D(O(x**n), x)
is probably what makes the most sense.
See issue 1756, comment 1 (http://code.google.com/p/sympy/issues/detail?id=1756#c1).
Aaron Meurer
>
>>>
>>>>>>>>> c = var("c")
>>>>>>>>> abs(x-c).series(x)
>>>>>> -(x - c)*sign(c)
>>>>>>
>>>> h[11] >>> abs(x-c).series(x)
>>>> Piecewise((x, -c == 0), (-(x - c)*sign(c), True))
>>>>
>>>
>>> abs(x) != x so this can't be right in the case c==0.
>>
>> If c is 0, abs(x-c) -> abs(x) which is x when approached from the
>> right.
>>
>>
>> /c
>>
>
>
On Feb 15, 2011, at 6:05 AM, Ronan Lamy wrote:
>>>> h[10] >>> Derivative(exp(x).series(), x).doit()
>>>> 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
>>>
>>> Wrong: the derivative of a O() term can be arbitrarily large.
>>
>> Can you give any direction as to what O(x**n).diff(x) should be?
>
> Basically, you can't say anything about O(x**n).diff(x). D(O(x**n), x)
> is probably what makes the most sense.
See issue 1756, comment 1 (http://code.google.com/p/sympy/issues/detail?id=1756#c1).
If this is the only thing holding this up I can remove the _eval_derivative form order and make sure that O(1) doesn't become 0.
No, exp(1/y) is O(1) for x going to 0, so you can't say that diff(O(1,
x), y) is 0.
First, I don't see how changing `O(x**2)` to `O(x)` says anything about how large that term might be. The only difference is in the rate at which it is growing. But I would agree that O(1) should not become 0. On the other hand...Doesn't it matter that one is differentiating the series and not the original function? If something is O(1) and I differentiate wrt some variable it should be zero because a constant doesn't depend on x. Similarly, if I have `O(x**2)` and differentiate, the error of the series will no longer grow as `x**2`, it will grow as O(x). The converse is that if I have a series that is O(x) and I multiply by x, the resulting expression will have errors that grow as `O(x**3)`. So doesn't this boil down to the difference between `diff(sin(x**2).series(x), x)` vs `diff(sin(x**2), x).series(x)`.
See the issue comment I linked to above. If something is O(1) in a variable (say x), then the derivative with respect to x could be unbounded. Now, the derivative with respect to another variable on the other hand should probably be 0.
By themselves, Order terms aren't required to be of the form O(x**n).
What should appear in the return value of series() is another matter,
which depends on the exact purpose of the method. According to its
docstring, it's supposed to return the Laurent series of the function,
so it's only defined for analytic functions, which means that it should
raise an exception in your example. I don't feel that such a restriction
is desirable, but I don't see how we can define series() without
ambiguity otherwise.
No, exp(1/y) is O(1) for x going to 0, so you can't say that diff(O(1,
x), y) is 0.
No, that's not exactly what it means (cf.
http://en.wikipedia.org/wiki/Big_O_notation ). 'cos(x**2) = O(1)' (it
should more properly be written 'cos(x**2) in O(1)') means that
cos(x**2)/1 is bounded near x=0. And once you've taken the series
expansion, you don't know where it came from (it could have been
cos(1/x**2) which is also bounded near x=0) so you can't say anything
about the size of the derivative.
>
> Take the n=1 approximation which is 1 + O(x). To me this means that
> the error between cos() and 1 is going to grow linearly with x. Of
> course we know there is a range in which this is true -- it is no
> longer true at x=2pi, but it is true near x=0. If I differentiate wrt
> x I am asking "How does the approximation change as I change x?" and
> the answer is, "It's constant: the error is growing at a constant rate
> as you change x _in the vicinity in which the series is valid_." Hence
> the expansion for the derivative of 1 + O(x) should be O(1).
The n=1 approximation of cos(x**2) is 1. '1 + O(x)' is the set of all
expressions whose approximation up to a linear term is 1. To answer the
question you ask, you need to study cos(x**2)-1, not 1+O(x), because in
the latter you've thrown away the information that the expression you're
looking at is cos(x**2).
HTH
Ronan
If both x and y go to 0, then there's no way to tell how exp(x/y)
behaves, since it depends on how x and y vary wrt each other, and in
that case series() can't do anything useful except returning exp(x/y)
unchanged or, possibly, returning the trivial expansion O(exp(x/y), x,
y). In any case, O(1) doesn't mean anything if no variables are
specified (beware though that in some cases the pretty-printer doesn't
show the variable although it exists).
Anyway, my point was just that if you do exp(x/y).series(x, n=0), you
should get O(1, x) since y is a constant in that expression, but
diff(exp(x/y), y) is clearly not 0.
>
> Do you have an example that demonstrates why O(x).diff(x) should not
> become O(1)? (And I guess we are assuming hee O(x, x) not O(x, x, y).)
x*sin(1/x) is in O(x) but diff(x*sin(1/x), x) == -cos(1/x)/x + sin(1/x)
isn't in O(1).
I can see that there are some misunderstandings about the O(x)
notation in this thread. I have tried to exactly define it here in the
docstring of Order:
https://github.com/sympy/sympy/blob/master/sympy/series/order.py#L26
with examples. So let's use that and resolve all the questions in this
thread and then make more examples in that docstring. I know it is a
little confusing at first, but the O notation is very rigorously
defined and there should be no ambiguity.
As to: O(1).diff(x), we need to put this into the docstring and put
there Ronan's example with: x*sin(1/x)
Ondrej
I mean O(x).diff(x), sorry.
Ondrej
No, that's not exactly what it means (cf.
http://en.wikipedia.org/wiki/Big_O_notation ). 'cos(x**2) = O(1)' (it
should more properly be written 'cos(x**2) in O(1)') means that
cos(x**2)/1 is bounded near x=0.
And once you've taken the series
expansion, you don't know where it came from (it could have been
cos(1/x**2) which is also bounded near x=0) so you can't say anything
about the size of the derivative.
>
> Take the n=1 approximation which is 1 + O(x). To me this means that
> the error between cos() and 1 is going to grow linearly with x. Of
> course we know there is a range in which this is true -- it is no
> longer true at x=2pi, but it is true near x=0. If I differentiate wrt
> x I am asking "How does the approximation change as I change x?" and
> the answer is, "It's constant: the error is growing at a constant rate
> as you change x _in the vicinity in which the series is valid_." Hence
> the expansion for the derivative of 1 + O(x) should be O(1).
The n=1 approximation of cos(x**2) is 1. '1 + O(x)' is the set of all
expressions whose approximation up to a linear term is 1. To answer the
question you ask, you need to study cos(x**2)-1, not 1+O(x), because in
the latter you've thrown away the information that the expression you're
looking at is cos(x**2).
HTH
Ronan
Here is a definition:
https://github.com/sympy/sympy/blob/master/sympy/series/order.py#L26
So f(x) = O(1) means that
|f(x)| <= M near x = 0, for some constant and finite M.
So what it means is that the value of f(x) around 0 is finite. That is
all that it says. As to your statement "you will never be more than a
constant away from the actual value", if by that statement you mean
that |f(x)| <= M for some M, then yes.
Ondrej
As to O(x).diff(x), that is tricky. Using the above definition, f(x) =
O(x) means:
|f(x)| <= M |x|
so this by itself says *nothing* about the derivative, and Ronan's
example shows why. However, the above is equivalent to:
lim_{x->0} |f(x) / x | <= M
and then, by applying l'Hospital rule (given some assumptions!):
lim_{x->0} |f(x) / x | = lim_{x->0} |f'(x) / x' | = lim_{x->0}
|f'(x) | <= M
so in other words, we have derived that:
|f'(x)| <= M
in other words, f'(x) = O(1), in other words O(x).diff(x) = O(1),
given the assumptions (very important) of the l'Hospital rule.
Now, let's look at those assumptions:
http://mathworld.wolfram.com/LHospitalsRule.html
the assumption is that f'(x)/x' exists and is finite. x' = 1, so that
is fine. So in other words, f'(x) must exist and be finite. For things
like f(x) = sin(x), that surely holds. For f(x) = x * sin (1/x), that
doesn't hold, because f'(x) = +/- oo, so it is not finite (it doesn't
even exist).
So the conclusion is that in general, O(x).diff(x) cannot be
determined, but if the derivative of that expression (that it
represents) happens to be finite, then indeed O(x).diff(x) = O(1).
After polishing the above argument, it should be put into the docstring.
Ondrej
|f(x)| <= M |x|
so this by itself says *nothing* about the derivative, and Ronan's
example shows why.
I really think we need to differentiate between f(x) and s(x), the series representation of f(x) in some order. There are many f(x) that can be represented by a single s(x) so I don't understand why everyone wants to treat differentiating s(x) as being contingent on the behavior of f(x). What confounds this issue is that most of the time this _is_ true (given the constraints you lay out below):e.g. sin(x) = x - x**3/3! + O(x**5)Differentiating the left gives cos(x) whose series is 1-x**2/2+O(x**4) and that is the same as you get when differentiating the right. But when you get down to O(1) the two are not the same. Let's take the example Ronan gives, x*sin(1/x), and exp(-x). The leading term of both is 1, isn't it? I generate the series for the former from the formula given by wolfram alpha:>>> for i in range(3):... print (x*Sum((-1)**k*(1/x)**(1+2*k)/factorial(1+2*k),(k,0,i)).doit()).expand()...11 - 1/(6*x**2)1 + 1/(120*x**4) - 1/(6*x**2)So both functions have (as a first approximation) s(x) = O(1). Asserting that O(1).diff(x) has two different values because the two functions that generated that s(x) have two different behaviors is like saying that electrons are colored and you can differentiate between H's and Cl's electrons in a bond. They don't. And differentiating s(x) = O(1) shouldn't depend on which function generated it.For most cases f(x).diff(x).series(x) = f(x).series(x).diff(x) but when you get down to O(1) the two are not the same. The operations are non-commutative.
After polishing the above argument, it should be put into the docstring.
I won't be that person, I guess, unless we can come to some consensus as to why s(x) and it's derivative needs be tied to a function from which it was derived. The only argument I can think of (and I am open to others) is that s(x)' represents f(x)' everywhere else so it should at O(1). I can understand that and -- I don't know why someone would be differentiating O(1) anyway -- I could see returning D(O(1), x) in this case instead of 0. That would solve the ills of O(1) not containing information about multiple variables as in the case of exp(x/y) which is O(1, x) but O(exp(x/y), y) for n=0.The separate issue, then, is whether O(x).diff(x) is defined or not. Can you give an example of an O(x) expression that doesn't meet the criteria you gave, Ondrej? The x*sin(1/x) doesn't work because (if I got the series correct) it doesn't have an O(x) term.I'm hamstrung for the time being, too, being unable to access my home computer which is in repair./c
What do you mean "the two are not the same"? They are the same. By
differentiating:
sin(x) = x + O(x**2)
cos(x) = 1 + O(x)
-sin(x) = O(1)
-cos(x) = O(1)
sin(x) = O(1)
>> gives, x*sin(1/x), and exp(-x). The leading term of both is 1, isn't it? I
>> generate the series for the former from the formula given by wolfram alpha:
>> >>> for i in range(3):
>> ... print
>> (x*Sum((-1)**k*(1/x)**(1+2*k)/factorial(1+2*k),(k,0,i)).doit()).expand()
>> ...
>> 1
>> 1 - 1/(6*x**2)
>> 1 + 1/(120*x**4) - 1/(6*x**2)
>> So both functions have (as a first approximation) s(x) = O(1). Asserting
>> that O(1).diff(x) has two different values because the two functions that
>> generated that s(x) have two different behaviors is like saying that
>> electrons are colored and you can differentiate between H's and Cl's
>> electrons in a bond. They don't. And differentiating s(x) = O(1) shouldn't
>> depend on which function generated it.
What I am saying is that O(x) can represent all kinds of functions,
from the definition in my previous emails. Some of these functions
have derivatives, and for those O(x).diff(x) = O(1). Some other
functions don't have derivatives, and for those O(x).diff(x) can be
infinite or not defined.
>> For most cases f(x).diff(x).series(x) = f(x).series(x).diff(x) but when
>> you get down to O(1) the two are not the same. The operations are
They are the same.
>> non-commutative.
>>>
>>> After polishing the above argument, it should be put into the docstring.
>>>
>>
>> I won't be that person, I guess, unless we can come to some consensus as
>> to why s(x) and it's derivative needs be tied to a function from which it
>> was derived. The only argument I can think of (and I am open to others) is
They are not tied to the function from which it was derived.
>> that s(x)' represents f(x)' everywhere else so it should at O(1). I can
>> understand that and -- I don't know why someone would be differentiating
>> O(1) anyway -- I could see returning D(O(1), x) in this case instead of 0.
O(1).diff(x) is O(1) again.
>> That would solve the ills of O(1) not containing information about multiple
>> variables as in the case of exp(x/y) which is O(1, x) but O(exp(x/y), y) for
>> n=0.
Let's talk about one variable for now, so that we don't complicate matters.
>> The separate issue, then, is whether O(x).diff(x) is defined or not. Can
>> you give an example of an O(x) expression that doesn't meet the criteria you
>> gave, Ondrej? The x*sin(1/x) doesn't work because (if I got the series
>> correct) it doesn't have an O(x) term.
Oops, you are right. The
x*sin(1/x) = x * (1/x + O(1/x**3)) = 1 + O(1/x**2)
so by differentiating, you get:
(x*sin(1/x))' = 0 + O(1/x**3)
which agrees with Ronan's formula (just expand the right hand side
into a series, or paste it to wolfram alpha):
diff(x*sin(1/x), x) == -cos(1/x)/x + sin(1/x)
it simply behaves as O(1/x**3) around origin.
An example when O(x).diff(x) isn't defined is for example:
f(x) = x**2 * sin(1/x)
obviously:
f(x) = x**2 * sin(1/x) = x**2 * (1/x + O(1/x**3)) = x + O(1/x)
f'(x) = 2*x * sin(1/x) - cos (1/x) = 1 + O(1/x**2)
you can differentiate either side, it works. The above is very
explicit way. Now you can do the same with less terms (and you get
O(x) here):
f(x) = x**2 * sin(1/x) = x**2 * (1/x + O(1/x**3)) = x + O(1/x) = O(x)
f'(x) = 2*x * sin(1/x) - cos (1/x) = 1 + O(1/x**2) = O(1)
I think that you can write f'(x) as O(1) after all. But you have to
understand, that neither O(x) for f(x) nor O(1) for f'(x) represent
finite expressions. In fact, as you can see above the innocent O(x) is
actually infinite, because it represents x + O(1/x) which goes to oo
for x->0.
So actually, I think there is another little detail --- x * sin(1/x)
series expansion around the point x=0 doesn't converge for x->0
(neither the value, nor the derivative). It only converges for x close
to 0, e.g. I think the expansion is actually around the point x=oo,
and it happens to be valid in the interval (0, oo).
>> I'm hamstrung for the time being, too, being unable to access my home
>> computer which is in repair.
Cool, at least you can think about this and get it resolved first. :)
Ondrej
So the x * sin(1/x) is a bad example, because it doesn't have *any*
expansion around x = 0. Only around x=oo, which is called asymptotic
series and I never got to implement it in sympy, but it should be
quite easy --- one needs to look into the Gruntz thesis and use some
code from the limits.
At the moment, I don't have any example when O(x).diff(x) is not
defined. I think that always O(x).diff(x) = O(1). Around x=0, assuming
both sides are defined.
Ondrej
Ok, I think I have resolved this confusion.
x * sin(1/x) is indeed O(x) around x=0 (and if something is O(x), it
is also O(1)). However, and that is important, the series expansion is
not defined around x=0. And if it is not defined, it doesn't make any
sense to use O(x), as that is only used in series expansion.
But, if there is a series around x=0, then I think always O(x).diff(x) = O(1).
Ronan, what do you think?
Ondrej
This is the question of the day: should O(1).diff(x) be zero or not? There are two possibilities:
A) you treat O(1) like any other expression;
it has not dependence on x;
it's derivative is 0
B) you treat O(1) like a representation of some function
differentiating this is like differentiating the orginal function
we don't know what that might be
we leave it alone and return O(1)
>>>>> I won't be that person, I guess, unless we can come to some
>>>>> consensus as
>>>>> to why s(x) and it's derivative needs be tied to a function from
>>>>> which it was derived. The only argument I can think of (and I am
>>>>> open to others) is
>>>
>>> They are not tied to the function from which it was derived.
They are if you say that O(1).diff(x) is defined in one case but
not another. But see next.
>>>
>>> O(1).diff(x) is O(1) again.
OK, this is the first time I think anyone has said so explicitly
to leave O(1) alone. But perhaps that what Ronan meant when he said
that O(1).diff(x) can have any value.
>>>
>>> An example when O(x).diff(x) isn't defined is for example:
>>>
>>> f(x) = x**2 * sin(1/x)
When sympy can return an expansion for that perhaps we can
worry about the O(x).diff(x) issue, but for now it generates
a PoleError.
>>
>> At the moment, I don't have any example when O(x).diff(x) is not
>> defined. I think that always O(x).diff(x) = O(1). Around x=0,
>> assuming both sides are defined.
>
> Ok, I think I have resolved this confusion.
>
> x * sin(1/x) is indeed O(x) around x=0 (and if something is O(x), it
> is also O(1)). However, and that is important, the series expansion is
> not defined around x=0. And if it is not defined, it doesn't make any
> sense to use O(x), as that is only used in series expansion.
Again...sympy doesn't return anything so this is a non-issue today.
>
> But, if there is a series around x=0, then I think always
> O(x).diff(x) = O(1).
>
OK, so I removed the line that was making O(1).diff(x) be zero and
left in differentiation of O(x).diff(x) = 1. Are we back on track again
except for a docstring somewhere explaining why O(1).diff(x) is not
zero? See my commit message to see if something like that can be said.
/c
O(1) is ambiguous. In expressions like O(x**3), it is automatically
treated as O(x**3, x). For O(1), you should write O(1, x), or O(1, y),
to make this clear. Then O(1, x).diff(y) = 0, but O(1, x).diff(x) =
O(1)
> B) you treat O(1) like a representation of some function
> differentiating this is like differentiating the orginal function
> we don't know what that might be
> we leave it alone and return O(1)
See above.
>
>
>>>>>> I won't be that person, I guess, unless we can come to some
>>>>>> consensus as
>>>>>> to why s(x) and it's derivative needs be tied to a function from
>>>>>> which it was derived. The only argument I can think of (and I am
>>>>>> open to others) is
>>>>
>>>> They are not tied to the function from which it was derived.
>
> They are if you say that O(1).diff(x) is defined in one case but
> not another. But see next.
>
>>>>
>>>> O(1).diff(x) is O(1) again.
>
> OK, this is the first time I think anyone has said so explicitly
> to leave O(1) alone. But perhaps that what Ronan meant when he said
> that O(1).diff(x) can have any value.
>
>>>>
>>>> An example when O(x).diff(x) isn't defined is for example:
>>>>
>>>> f(x) = x**2 * sin(1/x)
>
> When sympy can return an expansion for that perhaps we can
> worry about the O(x).diff(x) issue, but for now it generates
> a PoleError.
Yep, it is a non issue for now.
>
>>>
>>> At the moment, I don't have any example when O(x).diff(x) is not
>>> defined. I think that always O(x).diff(x) = O(1). Around x=0,
>>> assuming both sides are defined.
>>
>> Ok, I think I have resolved this confusion.
>>
>> x * sin(1/x) is indeed O(x) around x=0 (and if something is O(x), it
>> is also O(1)). However, and that is important, the series expansion is
>> not defined around x=0. And if it is not defined, it doesn't make any
>> sense to use O(x), as that is only used in series expansion.
>
> Again...sympy doesn't return anything so this is a non-issue today.
>
>>
>> But, if there is a series around x=0, then I think always
>> O(x).diff(x) = O(1).
>>
>
> OK, so I removed the line that was making O(1).diff(x) be zero and
> left in differentiation of O(x).diff(x) = 1. Are we back on track again
Do you mean O(x).diff(x) = O(1) ? It definitely is not just "1".
> except for a docstring somewhere explaining why O(1).diff(x) is not
> zero? See my commit message to see if something like that can be said.
Looks good.
Let's wait for Ronan, maybe we overlooked something.
Ondrej
>
> e.g. sin(x) = x - x**3/3! + O(x**5)
>
>
> Differentiating the left gives cos(x) whose series is 1-x**2/2
> +O(x**4) and that is the same as you get when differentiating
> the right. But when you get down to O(1) the two are not the
> same. Let's take the example Ronan gives, x*sin(1/x), and
> exp(-x). The leading term of both is 1, isn't it? I generate
> the series for the former from the formula given by wolfram
> alpha:
>
>
> >>> for i in range(3):
> ... print (x*Sum((-1)**k*(1/x)**(1+2*k)/factorial(1
> +2*k),(k,0,i)).doit()).expand()
> ...
> 1
> 1 - 1/(6*x**2)
> 1 + 1/(120*x**4) - 1/(6*x**2)
>
No, 1/x -> oo, and you can't approximate sin by a series at infinity,
and it doesn't even have a limit. However, for all real x, |sin(1/x)| <
1, so sin(1/x) is in O(1) and x*sin(1/x) is in O(x).
>
> For most cases f(x).diff(x).series(x) = f(x).series(x).diff(x)
> but when you get down to O(1) the two are not the same. The
> operations are non-commutative.
Your argument is circular, you can't assume that <some series>.diff(x)
has some specific definition to argue how the same expression should be
defined.
Anyway, I agree that the operations are non-commutative. Actually, once
you've taken the series, you've thrown away the information that allows
you to say anything useful about the derivative (the only thing you can
say is that if you integrate the derivative back, you'll get something
in O(1)), which makes differentiation nearly meaningless.
We should make O like Poly and require a symbol when it can't be inferred.
Aaron Meurer
Since I've read this thread, I thought I would leave my 2 cents.
في ث، 15-02-2011 عند 21:13 -0800 ، كتب Ondrej Certik:
> Ok, I think I have resolved this confusion.
>
> x * sin(1/x) is indeed O(x) around x=0 (and if something is O(x), it
> is also O(1)). However, and that is important, the series expansion is
> not defined around x=0. And if it is not defined, it doesn't make any
> sense to use O(x), as that is only used in series expansion.
I think I'm going to quote Ronan for this, he has explained it well:
> By themselves, Order terms aren't required to be of the form O(x**n).
> What should appear in the return value of series() is another matter,
> which depends on the exact purpose of the method.
So the thing we should be debating is the purpose of series(), and the
purpose of the Order class.
Again, Ronan said:
> According to its
> docstring, it's supposed to return the Laurent series of the function,
> so it's only defined for analytic functions, which means that it
> should raise an exception in your example.
A function f is said to be analytic at a given point x_0 if it can be
written as a power series (in the form \sum_{n=0}^\infty a_n (x-x_0)^n)
in a neighbourhood of that point (you can think of this as a "small"
interval containing x_0). The Taylor series is then defined as
\sum_{n=0}^\infty (f^{(n)}(x_0)/n!) (x-x_0)^n (where f^{(n)} is the n-th
derivative). In this case, we have:
f(x) = \sum_{j=0}^n a_n (x-x_0)^j + O((x-x_0)^n+1)
(or o((x-x_0)^n) since we generally use little-o for this).
A Laurent series can be defined if the function happen to have a
singularity, but is otherwise analytic (e.g. when doing an expansion
near a pole). It is in the form \sum_{n \in Z} a_n (x-x_0)^n.
So to go back to your example, x*sin(1/x) is actually O(x) for x real,
but is unbounded near 0 if we consider x to be complex, and then 0 is a
singularity, and the expansion has an infinite number of 1/(x-x_0)^n
terms. The question is, as Ronan pointed out, what should sympy do, and
when should it raise an exception.
> But, if there is a series around x=0, then I think always O(x).diff(x) = O(1).
If we have f an analytic function near 0, and f(x) = s(x) + O(x), then
yes, the derivative of (f(x) - s(x)) is indeed O(1) near 0. The problem
is that the big-O notation can be used for any function, not just
"regular" functions, so if we restrict its use to series expansion for
analytic functions, we can do that.
HTH,
Abderrahim
P.S. maybe wikipedia explains things better than I do, see
http://en.wikipedia.org/wiki/Laurent_series and
http://en.wikipedia.org/wiki/Taylor_series
+1
Yes, thanks for looking at my arguments. So we seem to agree, that
a) if we restrict ourselves to only functions that can be expanded
using a series() method (e.g. Laurent series), then *always*
O(x).diff(x) = O(1).
b) currently the O(x) is only returned by the series() class, so if we
restrict the O(x) usage to only functions that can be expanded (and we
can discuss whether or not to do so --- I vote to do so), then all is
fine, and things should work.
As to the x * sin(1/x) example, Wolfram Alpha returns a series
expansion around x = oo. This is not implemented in SymPy yet, but in
the future it will. I think that around x=oo, the function is
perfectly analytic and again, there will be no problem. Around x=0,
the function is not analytic, and thus series() around zero must raise
an exception. And thus there is no problem with O(x).diff(), because
O(x) is not defined for x * sin(1/x) around x=0.
Sorry for the reiteration, I just want to make sure we all understand
the problem now (I myself got little confused:).
So I vote for:
* O(x) be defined only for analytic functions
* O(x).diff() = O(1, x)
* O(1) is ambiguous and should probably raise an exception (unless we
find some other problem with it), and one should write O(1, x), or
O(1, y). It is ok to write just O(x), or O(x**2)
* there are some more nasty functions, where it makes sense to use
O(x**(1/2)) for example, this one:
http://www.wolframalpha.com/input/?i=sqrt%28sin%28x%29%29
we currently use:
>>> print sqrt(sin(x)).series(x)
x**(1/2) - x**(5/2)/12 + x**(9/2)/1440 + O(x**6)
>>> print sqrt(sin(x)).series(x, 0, 12)
x**(1/2) - x**(5/2)/12 + x**(9/2)/1440 - x**(13/2)/24192 -
67*x**(17/2)/29030400 - x**(21/2)/5677056 + O(x**12)
which I think *is* correct. But it might make sense to return
O(x**(23/2)) instead of O(x**12) in the last one, just like wolfram
alpha.
Ondrej
^^^^ Sorry, I should have written (to be absolutely clear):
And thus there is no problem with O(x).diff(), because
O(x) is not defined (assuming we only define O(x) for *analytic*
functions) for x * sin(1/x) around x=0.
Ondrej
Once everything is cleared here, I am going to write up a nice sphinx
docs about this myself, with examples from this thread. I remember
when I was designing the O(x) class, I had to spent quite some time on
this, and I wrote that docstring. This thread has raised other issues,
that are clearly not clarified at all by that docstring.
One more question: if I write the equation:
O(x).diff(x) = O(1)
assuming the definition of O(x) from wikipedia (e.g. x * sin(1/x) is
allowed), then there are only two possibilities:
1) the derivative of O(x) is defined, in which case the equation above holds
2) the derivative of O(x) is not defined, in which case, the equation
doesn't make any sense, because the left hand side is not defined
Is it really that simple, or is there some counter example?
Ondrej
> As to the x * sin(1/x) example, Wolfram Alpha returns a series
> expansion around x = oo. This is not implemented in SymPy yet, but in
> the future it will. I think that around x=oo, the function is
> perfectly analytic and again, there will be no problem. Around x=0,
> the function is not analytic, and thus series() around zero must raise
> an exception.
Yes, it is not analytic in 0, but it does have Laurent series expansion
near 0. (which is the same as the series given by wolfram alpha).
> And thus there is no problem with O(x).diff(), because
> O(x) is not defined for x * sin(1/x) around x=0.
>
> Sorry for the reiteration, I just want to make sure we all understand
> the problem now (I myself got little confused:).
>
> So I vote for:
>
> * O(x) be defined only for analytic functions
> * O(x).diff() = O(1, x)
> * O(1) is ambiguous and should probably raise an exception (unless we
> find some other problem with it), and one should write O(1, x), or
> O(1, y). It is ok to write just O(x), or O(x**2)
> * there are some more nasty functions, where it makes sense to use
> O(x**(1/2)) for example, this one:
I believe your last point contradicts the first :-)
So either we work only with analytic functions thus excluding "nasty"
ones (and these are relatively common), or we need to find something
else.
Another point is what should series() compute? I was assuming (as that's
what I've ever seen in my studies) that we would only ever get x**n
terms, which seems to be wrong. So we first need to define what are
series sympy is supposed to compute, and whether they should have their
own functions or be stuffed in the series method, then we can discuss
what Order is supposed to do.
Regards,
Abderrahim
Laurent series. The O(x**(1/2)) is there simply because the whole
function, that you are calculating, is multiplies by x**(1/2). E.g.
compare:
http://www.wolframalpha.com/input/?i=sin%28x%29
vs.
http://www.wolframalpha.com/input/?i=sqrt%28x%29+*+sin%28x%29
there is no magic behind this. I think it is still Laurent series.
> own functions or be stuffed in the series method, then we can discuss
> what Order is supposed to do.
Good points.
We definitely need to calculate Laurent series. Example: e = x * sin(1/x)
1) e.series(x, 0, 10) should raise an exception.
2) e.series(x, oo, 10) would return the first 10 terms of the Laurent
series, that you are talking about (that probably converges on the
interval (0, oo) )
So there is no problem with this, is it?
As to the O() notation, there is one question that I am not sure at
the moment ---- in sympy, it is strictly defined around x=0. Can all
series expansion for points x != 0 be defined in terms of O(), defined
around 0? I think it can, by simply doing O( (x-5)**2 ), to express
the term x**2 around x=5. And in the infinity, you simply do
O(1/x**2). That way, all O() symbols are around the point x=0, and
there is no confusion.
As to whether we need more than Laurent series, it is needed in the
limits for some nasty ones I think. But there might be some other way
to do the limits, so in principle that can be somehow solved (in the
worst case introducing some new series expansion code just for
limits).
Ondrej
No. By definition, there are only integer powers in a Laurent series.
sqrt isn't analytic in 0 because it's not continuous on any complex
neighbourhood of 0 - it has a branch cut.
> > own functions or be stuffed in the series method, then we can discuss
> > what Order is supposed to do.
>
> Good points.
>
> We definitely need to calculate Laurent series. Example: e = x * sin(1/x)
>
> 1) e.series(x, 0, 10) should raise an exception.
> 2) e.series(x, oo, 10) would return the first 10 terms of the Laurent
> series, that you are talking about (that probably converges on the
> interval (0, oo) )
>
> So there is no problem with this, is it?
>
> As to the O() notation, there is one question that I am not sure at
> the moment ---- in sympy, it is strictly defined around x=0. Can all
> series expansion for points x != 0 be defined in terms of O(), defined
> around 0? I think it can, by simply doing O( (x-5)**2 ), to express
> the term x**2 around x=5. And in the infinity, you simply do
> O(1/x**2). That way, all O() symbols are around the point x=0, and
> there is no confusion.
We still need to specify which function goes to which limit. O(x, x->0)
isn't the same thing as O(x, x->1). More critically, O(exp(1/x), x->0+)
isn't the same as O(exp(1/x), x->0-).
>
>
> As to whether we need more than Laurent series, it is needed in the
> limits for some nasty ones I think. But there might be some other way
> to do the limits, so in principle that can be somehow solved (in the
> worst case introducing some new series expansion code just for
> limits).
>
> Ondrej
>
That is right. So what is the name of the series produced by sqrt(sin(x)) ?
We definitely want those in sympy.
>
>> > own functions or be stuffed in the series method, then we can discuss
>> > what Order is supposed to do.
>>
>> Good points.
>>
>> We definitely need to calculate Laurent series. Example: e = x * sin(1/x)
>>
>> 1) e.series(x, 0, 10) should raise an exception.
>> 2) e.series(x, oo, 10) would return the first 10 terms of the Laurent
>> series, that you are talking about (that probably converges on the
>> interval (0, oo) )
>>
>> So there is no problem with this, is it?
>>
>> As to the O() notation, there is one question that I am not sure at
>> the moment ---- in sympy, it is strictly defined around x=0. Can all
>> series expansion for points x != 0 be defined in terms of O(), defined
>> around 0? I think it can, by simply doing O( (x-5)**2 ), to express
>> the term x**2 around x=5. And in the infinity, you simply do
>> O(1/x**2). That way, all O() symbols are around the point x=0, and
>> there is no confusion.
>
> We still need to specify which function goes to which limit. O(x, x->0)
> isn't the same thing as O(x, x->1). More critically, O(exp(1/x), x->0+)
> isn't the same as O(exp(1/x), x->0-).
Yes. That's why I am asking, whether it's enough to consider O(x,
x->0), resp. probably O(x, x->0+), and write everything else using it.
I think we can, just like any limit can be converted to lim_{x->0+},
as we do in limits.py. I would highly suggest that internally, O(x) is
only represented at x->0+, and then implement conversion rules to
convert from any other point/limit using the same rules as for limits.
Ondrej
As there is no x**(23/2) term shouldn't the order jump to the order of the first term not included and be O(x**(25/2))?
Of course, I wrote earlier that those questions that needed to be
discussed are separated from 2084, out of scope of your work here.
Now I see that some more questions are under discussion, but I can't
understand all of nuances of it - some of questions were on the wrong
track, some resolved.
So, please, can anyone produce some total or intermediate conclusion of
discussion ? May be in wiki form.
In form near by
issue:
- suggested variants what to do;
- or decision what to do;
- unresolved new questions and other remarks;
Some further remarks:
>>> _`Bases functions are not the powers of natural`
>>> ------------------------------------------------
>>>
>>> It is not a error.
>>>
>>> >>> (exp(x)*log(x)).series(n=3)
>>> x*log(x) + x**2*log(x)/2 + log(x) + O(x**3*log(x))
>>>
>>> But we must note that those cases are not real "natural power
>>> series".
>
> What is it that you would expect to see here? (This is the same sort of expresion obtained from wolfram alpha.)
>
It is right, as expected.
I only notice that it is not ordinary "natural power series".
I am afraid that in future internal "Series" class must be implemented
separatly of ordinary Expr to encapsulate some problems with series,
with some internal types of series (Laurent, Power, sqrt, abstract
Taylor expansion) with tracking of assumtions (x--> "+" or "-" x0),
Big_O, and so on. And with the operations upon this objects defined with
accuracy (summation, multiplication, derivation), and representation as
sum of terms and BigO notation.
And for returning to the normal sympy Expr some method must be used.
"Normal" in wolfram mathematica
"removeO" in sympy
"truncate" in sage
It is suggested variant for future road map.
>>> _`Incorrect power in Big-O expression`
>>> --------------------------------------------
>>>
>>> Typical examples are:
>>>
>>> >>> (exp(x)/x**2).series()
>>> 1/2 + x/6 + 1/x + x**(-2) + x**2/24 + x**3/120 + O(x**4)
>>> >>> (exp(x)*x**2).series()
>>> x**2 + x**3 + x**4/2 + x**5/6 + x**6/24 + x**7/120 + O(x**8)
>>> >>> (exp(x)/x**4).series()
>
> Could you please be explicit here? I'm not sure what is expected. I
can fix it if you can tell me what the answer should be.
.series() method by default must be return order O(x**6), so O(x**4) and
O(x**8) is incorrect.
Comparison that I wrote is for understanding in what places the error
may be hided in my supposition.
>>> _`Insufficient of operations with series`
>>> ------------------------------------------
>>>
>>> It is for a future.
>>> Some operation are work fine due to the fine "Order" class
>>> realization:
>>>
>>> #) multiplication "series" by "expr"
>>> >>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1,
>>> n=4)).expand() 8/9 + x/2 - 11*x**2/12 + 8*x**3/9 - x**4/2 + x**5/6 - x**6/36
>>>
>>> Expected "1 + O((x-1)**4)"
>
> I think this is an important point so I want to make sure I am understanding this because I'm not sure that I would expect to get a cubic from each expression that correctly matches exp(x) or exp(-x) at x=1. If I substituted x=1 into the result I would expect to see an exact match between the function and the series, and I do:
>
> >>> S('8/9 + x/2 - 11*x**2/12 + 8*x**3/9 - x**4/2 + x**5/6 - x**6/36').subs(x,1)
> 1
>
> I wouldn't expect to see only a constant 1 for the expansion. Perhaps the O(x**4) should eat all the terms except the 8*x**3/9, but since we don't retain an O() term for this case that can't happen and you will always have too many terms in the product.
>
>>>
>>> Compare with:
>>>
>>> >>> (exp(x).series(x, 1, n=4) * exp(-x).series(x, 1, n=4)).subs(x, x+1).expand()
>>> 1 - x**4/12 - x**6/36
>>>
>
> This is an entirely different thing, isn't it?. That expression represents exp(x)*exp(-x) at x=0...and plugging in x=0 must give 1 (as it does).
Well, I think that it is concerned with those themes:
1) Big-O at point x <> 0
2) Not canonical representation of terms (as (x-x0) powers)
3) Operations with series (which are allowed only between series
around the same x0 points)
If this questions will be resolved then issue will be resolved easy -
what terms of polynomes must be eaten and leaven.
>>> (Though I
>>> donn't clear understand the name-token.)
>
> It means "lazy series"...only a new term at a time is returned rather than many at once.
>
Understand.
Thanks.
> Sorry for the reiteration, I just want to make sure we all understand
> the problem now (I myself got little confused:).
>
> So I vote for:
>
> * O(x) be defined only for analytic functions
Sorry, I don't understand this proposition precisely.
Your proposition implicitly means that O(x) knows something about some
function from which the series expansion is obtained with this O(x).
So I specify, may be you mean that the series expansion (and with O(x)
in particular) is defined for only analytic functions?
Then, Abderrahim put a right question "Another point is what should
series() compute?"
Digress from theme, but I wrote earlier that the aims of series and
Big-O must be taken into consideration. For me it
1. symplify calculations with series (O eat terms)
2. Big-O signals for me do not forget terms of orders.
Then, return to question what O(x) knows about original function and
what to do with it.
I found in sympy/wiki [1] that the question have been raised by you how
to create Series class. I think that it will resolve most of problems in
this way:
1. series() return object of some kind according to context
- power series class
- Laurent class
- other classes
sub classes of main "Series" class
It is, of course, only forward operation - after it we can't know
nothing about original function, we know only that it is series, its
order, its variables, x0 point and order, so we do not worry about many
things.
2. there are some operation with it (respect to specific sub classes):
- summation
- multiplication
- integration derivation
they are admitted only if series are with the same x0 point and
with the same kind.
3. there is a representation method (print, pretty_print) as a
summation in canonical form, with Big-O notation. But it is not real
summation as sympy Expr.
4. there is removeO method to return to normal sympy Expr.
5. Big-O context is connected with Series class context (x0 point,
order, direction), so it behavior (and definition) is connected with
some specific Series class to.
6. some internal methods (coefficients without (x-x0)^n ) used for
more efficiently operations (multiplication of series for example)
I think that this way resolve in future most of problems, and moreover
also encapsulate them in this class.
[1] http://code.google.com/p/sympy/wiki/Discussion section "Series"
h[3] >>> e.series(x0=oo)
Well, I see that a lot of things was corrected in it (against master
errors), I can think out some more remarks infinitely, but I think we
must lean on this milestone result.
So can you, Chris, or somebody else explain me in a few words what does
impede for merging smichr/2084 branch with master now?
I'm still learning about how these projects work, but the general rule is that commits may be added provided they have been reviewed favorably by someone other than the author (you and Ondrej have reviewed this). In addition, there must be no objections.
Right now there is an objection by Ronan who said that series should not return a result without an O() (which is what nseries does when x0 != 0). In addition, Aaron hasn't responded to a request for comment on this. He is the one who has the ultimate say as to whether something is added or not (and is perhaps the person who can overrule a veto). If the changes are small and consistent with what he has been favorable to in the past then it can be pushed in without bothering him. In this case, however, the changes are large and we should wait for him to respond.
/c
The rule (you might think of it as the rule of thumb) is that there needs to be a consensus for a patch to be pushed in. If someone (in this case Ronan) doesn't like something, that is not a consensus, and the patch can't be pushed in until one is reached (and the patch reflects it).
If someone (for example in this case me) decides not to comment on a review, then it must be assumed that they had no objections to the patch, because otherwise that person should have said something. Of course, it can't be assumed that he is +1 on the patch automatically. The default is actually +0, because it's assumed that if someone doesn't comment on a review, then that person has not looked at the changes. That person simply is deciding to not take part in the consensus building this time around. Make sense?
The consensus does not need to include me every time (because I for one don't want to have to review every branch that goes through here). Normally, if a consensus is never reached, then a patch is just not pushed in. If there is a situation where one decision must be made one way or another, or we really want a fix to get in, I suppose that I would be the one to break the tie. But it's something that happens rarely, and something that I for one don't want to do very often.
Anyway, did you want me to comment on this? I don't remember any request for comment. I have been avoiding this rather complex review because it's a part of the code I don't really know, and you guys seemed to be handling it pretty well on your own.
Aaron Meurer
Thank you both for explanation.
As I understand Chris now can't yield result with an O() at the point <>
0, because there is no this appreciable object present in sympy yet.
And, secondary, because object O() that is present raise many errors in
internal implementation of limits and series.
Well, Chris have pulled the thread of many issues tangle, while he
solved the theme of limits ant point<>0.
Limits (level 1) use series (level 2), series use Big-O, so the last one
has a third level.
I think that problems must be logically separated to sub tasks, for the
aim to isolate the tasks logically , and for the aim to isolate task in
implementations.
And also for tactical organization of solving this tangle (what first,
what second, discussions, what is high priority, tests and so on).
It is clear that O() is logically separated from limits. But I see that
now chosen the tactic, to stay in smichr/2084 to resolve all problems.
(as proverb: "who lug, that he is loaded more"). I am afraid it will be
impossible to resolve all tangle by Chris in this way.
Though I see that Ondrej also begin reform O(x) to unhang this branch,
but I didn't found the separate issue for this as many others issues on
which we have stroken against. (it disorient me a little in discussion)
So, I am going to create wiki page saying
https://github.com/sympy/sympy/wiki/series-discussions where I place
some sections:
what problems we have now in series, what will be probably in the
future, what variants are suggested to resolve them.
Separate them logically for discussion (f.e. in mail list or in wiki).
It will be roughly page, but I hope that others will help me to continue.
Then continue what to do with this branch.
> now chosen the tactic, to stay in smichr/2084 to resolve all problems.
> (as proverb: "who lug, that he is loaded more"). I am afraid it will
> be impossible to resolve all tangle by Chris in this way.
I have proposed a workaround: 1) shift the origin to x0 and update the docstring to reflect the fact that this is what's happening 2) give the user an option to get an O() that looks right but isn't intended for anything else (see the most recent commit).
/c