Opened 9 years ago

Closed 5 months ago

Last modified 5 weeks ago

#12074 closed enhancement (fixed)

real nth root function

Reported by: burcin Owned by: burcin
Priority: minor Milestone: sage-9.2
Component: symbolics Keywords:
Cc: mjo, kcrisman, karsten.naert@…, eviatarbach, slelievre Merged in:
Authors: Burcin Erocal, Kwankyu Lee Reviewers: Karl-Dieter Crisman, Nils Bruin, Kwankyu Lee
Report Upstream: N/A Work issues:
Branch: f8cb7a0 (Commits) Commit:
Dependencies: Stopgaps:

Attachments (1)

trac_12074-nth_root.patch (2.6 KB) - added by burcin 9 years ago.

Download all attachments as: .zip

Change History (55)

comment:1 Changed 9 years ago by burcin

  • Cc kcrisman added
  • Work issues set to needs tests and documentation

comment:2 follow-up: Changed 9 years ago by jdemeyer

Is there any chance this could be made into a function that we can do calculus with, like computing derivatives, integrals, solving equations... (I'm afraid the answer will be no though because we need maxima).

Changed 9 years ago by burcin

comment:3 in reply to: ↑ 2 Changed 9 years ago by burcin

Replying to jdemeyer:

Is there any chance this could be made into a function that we can do calculus with, like computing derivatives, integrals, solving equations... (I'm afraid the answer will be no though because we need maxima).

This is already a symbolic function, so it plays well with symbolics generally (as opposed to piecewise functions for instance):

sage: v = nth_root(x,3)
sage: v*sin(x) + x^2
x^2 + real_nth_root(x, 3)*sin(x)

I updated the patch to add custom exponentiation and derivative methods as well:

sage: v^2
real_nth_root(x, 3/2)
sage: v*v
real_nth_root(x, 3/2)
sage: v.diff(x)
1/3*real_nth_root(x, -3/2)

This all needs a lot of work of course.

For integration and solving equations we call out to maxima. One way to get sensible results from these calls would be to convert this function to a regular (base)^(exp) representation when passing it to maxima. I don't think there is any way to read it back from the maxima result though.

comment:4 Changed 9 years ago by jdemeyer

  • Cc karsten.naert@… added

comment:5 Changed 7 years ago by eviatarbach

  • Cc eviatarbach added

comment:6 Changed 7 years ago by jdemeyer

  • Milestone changed from sage-5.11 to sage-5.12

comment:7 Changed 7 years ago by vbraun_spam

  • Milestone changed from sage-6.1 to sage-6.2

comment:8 Changed 7 years ago by vbraun_spam

  • Milestone changed from sage-6.2 to sage-6.3

comment:9 Changed 6 years ago by vbraun_spam

  • Milestone changed from sage-6.3 to sage-6.4

comment:10 Changed 18 months ago by slelievre

  • Cc slelievre added
  • Milestone changed from sage-6.4 to sage-wishlist

comment:11 Changed 6 months ago by kcrisman

There is some interest in this again, due to an upcoming book's 2nd edition.

Sympy's solution may be more elegant in some ways, because it uses Abs(x)**(1/3)*sign(x) for real_root(x,3), apparently. If that solution (in Sage, of course, with translations to Sympy) has better support overall in Sage we could just do that instead (for odd roots).

Here's the full Sympy code. They have better handling of complicated piecewise things, I guess.

    from sympy.functions.elementary.complexes import Abs, im, sign
    from sympy.functions.elementary.piecewise import Piecewise
    if n is not None:
        return Piecewise(
            (root(arg, n, evaluate=evaluate), Or(Eq(n, S.One), Eq(n, S.NegativeOne))),
            (Mul(sign(arg), root(Abs(arg), n, evaluate=evaluate), evaluate=evaluate),
            And(Eq(im(arg), S.Zero), Eq(Mod(n, 2), S.One))),
            (root(arg, n, evaluate=evaluate), True))
    rv = sympify(arg)
    n1pow = Transform(lambda x: -(-x.base)**x.exp,
                      lambda x:
                      x.is_Pow and
                      x.base.is_negative and
                      x.exp.is_Rational and
                      x.exp.p == 1 and x.exp.q % 2)
    return rv.xreplace(n1pow)

comment:12 Changed 6 months ago by klee

  • Authors changed from Burcin Erocal to Burcin Erocal; Kwankyu Lee
  • Branch set to public/12074

comment:13 Changed 6 months ago by git

  • Commit set to 5028e22e1d4bbd112a14983efd13f7aa43adf168

Branch pushed to git repo; I updated commit sha1. New commits:

5028e22Add real_nth_root symbolic function

comment:14 Changed 6 months ago by klee

Revived Burcin's implementation of real_nth_root symbolic function.

It is in public. Feel free to improve.

comment:15 Changed 6 months ago by klee

  • Keywords nth_root removed
  • Milestone changed from sage-wishlist to sage-9.2
  • Work issues needs tests and documentation deleted

comment:16 Changed 6 months ago by klee

  • Authors changed from Burcin Erocal; Kwankyu Lee to Burcin Erocal, Kwankyu Lee

comment:17 Changed 6 months ago by klee

  • Status changed from new to needs_review

comment:18 Changed 6 months ago by klee

  • Description modified (diff)

comment:19 follow-up: Changed 6 months ago by kcrisman

Kwankyu (관규?), thanks so much for getting that ball rolling, and the doctests/doc looks good so far.

Here are some questions/comments (for anyone, not necessarily for klee):

  • See this list of plot methods for this in the plot doc. We should add this, perhaps as the first-order approximation.
  • The file faq-usage.rst also should be amended since it mentions the same issue.
  • Maybe even there should be a plot example in this file, and then a link to this function from the plot doc?
  • Does this one work with derivatives/integrals? Maybe in the meantime we don't need the custom method.
  • Apparently Mathematica now has something like this, called Surd which, while perhaps technically a correct name, is not exactly inspiring to anyone who has taken math since 1950 ... Perhaps this is because Maple also has the same name though not capitalized. I like the Sage/Sympy name much better, but anyway these can be added to the conversions. (I don't think Maxima has an equivalent for now.)
Last edited 6 months ago by kcrisman (previous) (diff)

comment:20 follow-ups: Changed 6 months ago by mjo

  • Cc mjo added

The current behavior of integer/float exponentiation is due to python, I guess?

$ python
Python 3.7.7 (default, May  8 2020, 10:21:17) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> (-1)**(1/3)
(0.5000000000000001+0.8660254037844386j)

Otherwise the question can be asked: why not make that return real numbers when the argument is real? Which leads me to this question: would it make sense to call this function nth_root, and to have it return the real/complex answer depending on whether or not the argument is real/complex?

(I also like having the codomain of a function be explicit, so I'm not really advocating for this, just wondering out loud.)

comment:21 follow-up: Changed 6 months ago by nbruin

Some comments:

  • I think evalf needs some care to work properly on real ball fields and real interval fields.
  • I think we need a derivative implementation. I'd say just the formal rule d/dx real_nth_root(x,n) = 1/n*sgn(x)*real_nth_root(x,n)^(1/n-1). Naturally, we don't have to support differentiation with respect to the second variable.
  • I think real_nth_root(x,n)^m should not simplify to real_nth_root(x^m,n). For instance, for real_nth_root(-1,2)^2 this goes wrong.

comment:22 follow-up: Changed 6 months ago by kcrisman

More concretely, then, for this ticket:

  • Current branch + things that are probably necessary just to use:
    • Derivative and evaluation (can follow Sympy or Nils with the former, perhaps). Can we just use the syntax in Burcin's patch for the derivative, or do we no longer have that underscore method?
    • I can't remember if we support a custom integration but could include something in the documentation saying to use Sympy if you have to integrate?
    • Possibly fix the simplification - is the one in the branch the same as Burcin's, or Nils'? (Indeed, should we have this function work for n even and negative x?)
    • Might as well add the extra conversions.

After all, the point of this function (note how Sympy and the Ms handle this) is for something we know doesn't really behave well with respect to branch cuts etc., intentionally. So no one should necessarily be using it for production symbolics, but rather really mostly just for classroom usage in situations (which are legion) where we have to explicitly exclude complex stuff. Luckily for the logarithm this is a little easier to hide from the student, except for in an antiderivative of 1/x, but here it's not as "nice".

Then a separate ticket could do:

  • Improve things like simplification/codomains from the most necessary.

comment:23 Changed 5 months ago by git

  • Commit changed from 5028e22e1d4bbd112a14983efd13f7aa43adf168 to e188fa24b1857152b234551950eab526870811ca

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

e188fa2Add real_nth_root symbolic function

comment:24 in reply to: ↑ 19 Changed 5 months ago by klee

Replying to kcrisman:

Kwankyu (관규?)

Correct!

Here are some questions/comments (for anyone, not necessarily for klee):

Done.

  • The file faq-usage.rst also should be amended since it mentions the same issue.

Done.

  • Maybe even there should be a plot example in this file, and then a link to this function from the plot doc?

Made a link from the plot doc.

  • Does this one work with derivatives/integrals? Maybe in the meantime we don't need the custom method.

Almost.

sage: f = real_nth_root(x,3)
sage: f.diff()
1/3*real_nth_root(x^(-2), 3)
sage: f.integrate(x)
integrate(abs(x)^(1/3)*sgn(x), x)
sage: _.diff()
abs(x)^(1/3)*sgn(x)
  • Apparently Mathematica now has something like this, called Surd which, while perhaps technically a correct name, is not exactly inspiring to anyone who has taken math since 1950 ... Perhaps this is because Maple also has the same name though not capitalized. I like the Sage/Sympy name much better, but anyway these can be added to the conversions. (I don't think Maxima has an equivalent for now.)

Done. But I could not test them as I have no Mathematica nor Maple.

Last edited 5 months ago by klee (previous) (diff)

comment:25 in reply to: ↑ 20 Changed 5 months ago by klee

Replying to mjo:

The current behavior of integer/float exponentiation is due to python, I guess?

$ python
Python 3.7.7 (default, May  8 2020, 10:21:17) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> (-1)**(1/3)
(0.5000000000000001+0.8660254037844386j)

This convention is shared by other symbolic algebra systems.

comment:26 in reply to: ↑ 21 Changed 5 months ago by klee

Replying to nbruin:

Some comments:

  • I think evalf needs some care to work properly on real ball fields and real interval fields.

Now it works for real ball fields:

sage: a = RBF(-2)
sage: real_nth_root(a, 3)
[-1.259921049894873 +/- 3.92e-16]

and for real interval fields:

sage: a = RIF(-2)
sage: real_nth_root(a, 3)
-1.259921049894873?
  • I think we need a derivative implementation. I'd say just the formal rule d/dx real_nth_root(x,n) = 1/n*sgn(x)*real_nth_root(x,n)^(1/n-1). Naturally, we don't have to support differentiation with respect to the second variable.

I guess you meant d/dx real_nth_root(x,n) = 1/n*real_nth_root(x,n)^(1-n). Done.

  • I think real_nth_root(x,n)^m should not simplify to real_nth_root(x^m,n). For instance, for real_nth_root(-1,2)^2 this goes wrong.

real_nth_root(-1,2) now raises ValueError. The simplification is valid for other cases.

comment:27 in reply to: ↑ 22 Changed 5 months ago by klee

Replying to kcrisman:

After all, the point of this function (note how Sympy and the Ms handle this) is for something we know doesn't really behave well with respect to branch cuts etc., intentionally. So no one should necessarily be using it for production symbolics, but rather really mostly just for classroom usage in situations (which are legion) where we have to explicitly exclude complex stuff. Luckily for the logarithm this is a little easier to hide from the student, except for in an antiderivative of 1/x, but here it's not as "nice".

School math just works:

sage: f = real_nth_root(x^2, 2)
sage: g = f.diff()
sage: f.plot()
Launched png viewer for Graphics object consisting of 1 graphics primitive
sage: g.plot()
Launched png viewer for Graphics object consisting of 1 graphics primitive

comment:28 Changed 5 months ago by git

  • Commit changed from e188fa24b1857152b234551950eab526870811ca to 7cef50b2542c2b90e8a60b88beb6fc6d09d0d380

Branch pushed to git repo; I updated commit sha1. New commits:

7cef50bFix a typo in FAQ

comment:29 Changed 5 months ago by kcrisman

Okay, this is ready enough that I will (try to) upgrade my Sage develop branch just for this ticket. I still have a few thoughts - notably about seeing whether we can get Sympy to give us an integral for this, and about the derivative. Maybe we can get someone to even test out the M* conversion... Thanks for this work. Only took us how many years?

comment:30 Changed 5 months ago by git

  • Commit changed from 7cef50b2542c2b90e8a60b88beb6fc6d09d0d380 to d12ab31283e8bf8905fb1534d653df97619ba575

Branch pushed to git repo; I updated commit sha1. New commits:

d12ab31Fix for a pyflakes warning

comment:31 in reply to: ↑ 20 ; follow-up: Changed 5 months ago by klee

Replying to mjo:

Otherwise the question can be asked: why not make that return real numbers when the argument is real? Which leads me to this question: would it make sense to call this function nth_root, and to have it return the real/complex answer depending on whether or not the argument is real/complex?

It is possible to get this behavior

sage: f = x^(1/3)
sage: f(-8)
2*(-1)^(1/3)
sage: f(-2.)
0.629960524947437 + 1.09112363597172*I
sage: assume(x, 'real')
sage: g = x^(1/3)
sage: g(-2.)
-1.25992104989487
sage: g
real_nth_root(x, 3)
sage: g.diff()
1/3*real_nth_root(x^(-2), 3)
sage: g.plot()
Launched png viewer for Graphics object consisting of 1 graphics primitive

by injecting some code into expression.pyx. The question is whether we want this.

comment:32 in reply to: ↑ 31 ; follow-up: Changed 5 months ago by kcrisman

by injecting some code into expression.pyx. The question is whether we want this.

I'm gonna say no on that - not without some healthy sage-devel discussion. I do think some people use the assume command for precisely this purpose, though.

I'm going to look at this branch later today, thank you again for putting it in "modern" shape!

comment:33 follow-up: Changed 5 months ago by kcrisman

Here come a bunch of questions now that I have played around with this. They don't necessarily all need changes, but I think they are reasonable to ask even if they end up not needing change. But I'm not letting the perfect be the enemy of the good; as long as we document and open new tickets, that would be the important part.


  • Do we want a latex method? E.g. latex_name=r"\mathrm{abs}" used elsewhere in this file. This is obviously not ideal:
    sage: latex(F)
    {\rm real}_{{\rm nth}_{{\rm root}}}\left(x, 5\right)
    
    But I'm not sure whether just using the usual root syntax is "correct" either since we have a different function now. Using abs(x)^(1/3)*sgn(x) doesn't work for even n, else I'd recommend that for latex.
  • Integration, perhaps surprisingly, works, as you mention above:
    sage: f = real_nth_root(x,3)
    sage: f.diff()
    1/3*real_nth_root(x^(-2), 3)
    sage: f.integrate(x)
    integrate(abs(x)^(1/3)*sgn(x), x)
    sage: _.diff()
    abs(x)^(1/3)*sgn(x)
    
    Apparently integration now tries Maxima, Giac, and then Sympy, so that explains it. We should probably document this behavior.
  • Upon further consideration, maybe the derivative can be a little better. Because aren't we sort of assuming this is real input? Maybe we aren't. Here is Sympy.
    >>> from sympy import root, real_root, Rational
    >>> from sympy.abc import x, n
    >>> real_root(x,5)
    Piecewise((Abs(x)**(1/5)*sign(x), Eq(im(x), 0)), (x**(1/5), True))
    >>> diff(real_root(x,5),x)
    Piecewise((Abs(x)**(1/5)*Derivative(sign(x), x) + (re(x)*Derivative(re(x), x) + im(x)*Derivative(im(x), x))*sign(x)**2/(5*x*Abs(x)**(4/5)), Eq(im(x), 0)), (1/(5*x**(4/5)), True))
    >>> x = Symbol('x', real=True)
    >>> diff(real_root(x,5),x)
    2*Abs(x)**(1/5)*DiracDelta(x) + sign(x)**2/(5*Abs(x)**(4/5))
    
    and here is Sage
    sage: G = abs(x)^(1/5)*sgn(x)
    sage: G
    abs(x)^(1/5)*sgn(x)
    sage: G.diff(x)
    2*abs(x)^(1/5)*dirac_delta(x) + 1/10*(x + conjugate(x))*sgn(x)/abs(x)^(9/5)
    sage: G.diff(x).simplify()
    2*abs(x)^(1/5)*dirac_delta(x) + 1/5*x*sgn(x)/abs(x)^(9/5)
    
    at least
    sage: assume(x,'real')
    sage: F.diff(x)
    1/5*real_nth_root(x^(-4), 5)
    
    should mimic this, if not perhaps always. Any thoughts on this?
  • To elaborate, Sympy's function somehow allows complex input, but do we want to?
    sage: real_nth_root(3.*I,5)
    1.18476052767182 + 0.384952030759866*I # should this happen?
    
    That's of course related to Nils question regarding negative input too.
    sage: H = real_nth_root(x,2)
    sage: (H.subs(x=-1))^2
    ---------------------------------------------------------------------------
    ...
    ValueError: no real nth root of negative real number with even n
    sage: ((H)^2).subs(x=-1)
    1
    
    I'm not sure the latter should be allowed or not.
Last edited 5 months ago by kcrisman (previous) (diff)

comment:34 Changed 5 months ago by kcrisman

sage -t src/doc/en/faq/faq-usage.rst
**********************************************************************
File "src/doc/en/faq/faq-usage.rst", line 692, in doc.en.faq.faq-usage
Failed example:
    plot(x^(1/3), (x, -1, 1))
Expected nothing
Got:
    verbose 0 (3834: plot.py, generate_plot_points) WARNING: When plotting, failed to evaluate function at 100 points.
    verbose 0 (3834: plot.py, generate_plot_points) Last error message: 'can't convert complex to float'
    Graphics object consisting of 1 graphics primitive
**********************************************************************

So you'll have to make that not tested (I think there is a way to mark it as such other than # not tested but anyway...).

comment:35 Changed 5 months ago by kcrisman

  • Status changed from needs_review to needs_work

comment:36 in reply to: ↑ 32 ; follow-up: Changed 5 months ago by klee

Replying to kcrisman:

by injecting some code into expression.pyx. The question is whether we want this.

I'm gonna say no on that - not without some healthy sage-devel discussion. I do think some people use the assume command for precisely this purpose, though.

Injecting the code incur many doctest failures in other places. So I give up this idea.

comment:37 in reply to: ↑ 33 ; follow-up: Changed 5 months ago by klee

Replying to kcrisman:

  • Do we want a latex method? E.g. latex_name=r"\mathrm{abs}" used elsewhere in this file. This is obviously not ideal:
    sage: latex(F)
    {\rm real}_{{\rm nth}_{{\rm root}}}\left(x, 5\right)
    
    But I'm not sure whether just using the usual root syntax is "correct" either since we have a different function now. Using abs(x)^(1/3)*sgn(x) doesn't work for even n, else I'd recommend that for latex.

I see real_nth_root as representing the real function x1/n with variable x taking real numbers (possibly negative for odd n). The latex print is for humans. So why not just print x1/n?

  • Integration, perhaps surprisingly, works, as you mention above:
    sage: f = real_nth_root(x,3)
    sage: f.diff()
    1/3*real_nth_root(x^(-2), 3)
    sage: f.integrate(x)
    integrate(abs(x)^(1/3)*sgn(x), x)
    sage: _.diff()
    abs(x)^(1/3)*sgn(x)
    
    Apparently integration now tries Maxima, Giac, and then Sympy, so that explains it. We should probably document this behavior.

Done.

  • Upon further consideration, maybe the derivative can be a little better. Because aren't we sort of assuming this is real input? Maybe we aren't. Here is Sympy.
    >>> from sympy import root, real_root, Rational
    >>> from sympy.abc import x, n
    >>> real_root(x,5)
    Piecewise((Abs(x)**(1/5)*sign(x), Eq(im(x), 0)), (x**(1/5), True))
    >>> diff(real_root(x,5),x)
    Piecewise((Abs(x)**(1/5)*Derivative(sign(x), x) + (re(x)*Derivative(re(x), x) + im(x)*Derivative(im(x), x))*sign(x)**2/(5*x*Abs(x)**(4/5)), Eq(im(x), 0)), (1/(5*x**(4/5)), True))
    >>> x = Symbol('x', real=True)
    >>> diff(real_root(x,5),x)
    2*Abs(x)**(1/5)*DiracDelta(x) + sign(x)**2/(5*Abs(x)**(4/5))
    
    and here is Sage
    sage: G = abs(x)^(1/5)*sgn(x)
    sage: G
    abs(x)^(1/5)*sgn(x)
    sage: G.diff(x)
    2*abs(x)^(1/5)*dirac_delta(x) + 1/10*(x + conjugate(x))*sgn(x)/abs(x)^(9/5)
    sage: G.diff(x).simplify()
    2*abs(x)^(1/5)*dirac_delta(x) + 1/5*x*sgn(x)/abs(x)^(9/5)
    
    at least
    sage: assume(x,'real')
    sage: F.diff(x)
    1/5*real_nth_root(x^(-4), 5)
    
    should mimic this, if not perhaps always. Any thoughts on this?

I don't get your concern. I think there is nothing wrong with

sage: f = real_nth_root(x,3)
sage: f.diff()
1/3*real_nth_root(x^(-2), 3)
  • To elaborate, Sympy's function somehow allows complex input, but do we want to?
    sage: real_nth_root(3.*I,5)
    1.18476052767182 + 0.384952030759866*I # should this happen?
    
    That's of course related to Nils question regarding negative input too.
    sage: H = real_nth_root(x,2)
    sage: (H.subs(x=-1))^2
    ---------------------------------------------------------------------------
    ...
    ValueError: no real nth root of negative real number with even n
    sage: ((H)^2).subs(x=-1)
    1
    
    I'm not sure the latter should be allowed or not.

No. real_nth_root takes real input and outputs real. For the symbolic function 1/x, we assume x takes nonzero value. Likewise, we assume real value x for real_nth_root(x, 3).

comment:38 Changed 5 months ago by git

  • Commit changed from d12ab31283e8bf8905fb1534d653df97619ba575 to 00380b09135e8171593108059f17490a23f554ad

Branch pushed to git repo; I updated commit sha1. New commits:

00380b0Fixes for reviewer comments

comment:39 in reply to: ↑ 36 ; follow-up: Changed 5 months ago by mjo

Replying to klee:

Injecting the code incur many doctest failures in other places. So I give up this idea.

I was thinking of something simpler, like

sage: nth_root(RR(-1), 3)
-1.00000000000000
sage: nth_root(CC(-1), 3)
0.500000000000000 + 0.866025403784439*I

comment:40 in reply to: ↑ 37 ; follow-up: Changed 5 months ago by kcrisman

Thanks for all these fixes - we are converging :-) Couple more questions/comments:


  • I don't think you needed to make this one not tested; it should have passed fine. Only the subsequent one would have thrown an error.
    -    sage: plot(real_nth_root(x, 3), (x, -1, 1))
    -    Graphics object consisting of 1 graphics primitive
    +    sage: plot(real_nth_root(x, 3), (x, -1, 1))  # not tested
    
  • latex:

    I see real_nth_root as representing the real function x1/n with variable x taking real numbers (possibly negative for odd n). The latex print is for humans. So why not just print x1/n?

    It's a little tricky, because the humans it is intended for might not realize it's not quite the same. mjo or Nils, any thoughts? I can go with it for now.
  • calculus:

    Done.

    Thanks. However, that brings up the related issue that the online documentation is only going to show the _init_ method doc (and apparently not the tests?).

If you are up for it, I'd recommend making a more "narrative" structure which includes examples of calculus, plotting, etc., more like real_part does. If you'd prefer me to do that, I can - might just take a little longer as I recall how to add to a public branch :-)

  • diff output:
    sage: assume(x,'real')
    sage: F.diff(x)
    1/5*real_nth_root(x^(-4), 5)
    

    should mimic this, if not perhaps always. Any thoughts on this?

    I don't get your concern. I think there is nothing wrong with

    sage: f = real_nth_root(x,3)
    sage: f.diff()
    1/3*real_nth_root(x^(-2), 3)
    
    If n is odd, though, 2*abs(x)^(1/5)*dirac_delta(x) + 1/5*x*sgn(x)/abs(x)^(9/5) is somehow "better", and available. Just thinking out loud; could be for a followup ticket.
  • input:
    sage: H = real_nth_root(x,2)
    sage: (H.subs(x=-1))^2
    ---------------------------------------------------------------------------
    ...
    ValueError: no real nth root of negative real number with even n
    sage: ((H)^2).subs(x=-1)
    1
    

    I'm not sure the latter should be allowed or not.

    No. real_nth_root takes real input and outputs real. For the symbolic function 1/x, we assume x takes nonzero value. Likewise, we assume real value x for real_nth_root(x, 3).

    Wait, so are you saying that we should disallow that function with x=-1 to be evaluated? Because 1/x blows up if we inject x=0 - though then again Sage simplifies 1/x*x =1 which isn't technically correct. Anyway, I think this is Nils' point.

comment:41 in reply to: ↑ 39 ; follow-up: Changed 5 months ago by kcrisman

I was thinking of something simpler, like

sage: nth_root(RR(-1), 3)
-1.00000000000000
sage: nth_root(CC(-1), 3)
0.500000000000000 + 0.866025403784439*I

I feel like this would be another ticket, and need sage-devel discussion. This ticket is about adding something to aid in plotting, and then making sure it has enough bells and whistles that it doesn't end up causing confusion. I'm a little uncomfortable with this since

sage: RR(-1) == CC(-1)
True

and the point of the function is strictly for pedagogical purposes (which should also be made clear in the documentation).

comment:42 in reply to: ↑ 41 ; follow-up: Changed 5 months ago by mjo

Replying to kcrisman:

I feel like this would be another ticket, and need sage-devel discussion. This ticket is about adding something to aid in plotting, and then making sure it has enough bells and whistles that it doesn't end up causing confusion. I'm a little uncomfortable with this since

sage: RR(-1) == CC(-1)
True

and the point of the function is strictly for pedagogical purposes (which should also be made clear in the documentation).

We run into that same problem whenever we have a subclass method that does something different from its superclass method. I was only trying to clear up my previous suggestion. If the authors/reviewers don't think this is a good idea, feel free to ignore it.

comment:43 in reply to: ↑ 42 ; follow-up: Changed 5 months ago by kcrisman

I was only trying to clear up my previous suggestion.

Fair enough.

If the authors/reviewers don't think this is a good idea, feel free to ignore it.

If this gets merged, why not open a new ticket for an nth_root function if you think it's more generally useful? SymPy has some explicit warnings about these things for its function but we could follow that model (its function is intended for more than the current ticket, I believe).

comment:44 in reply to: ↑ 43 Changed 5 months ago by mjo

Replying to kcrisman:

If this gets merged, why not open a new ticket for an nth_root function if you think it's more generally useful? SymPy has some explicit warnings about these things for its function but we could follow that model (its function is intended for more than the current ticket, I believe).

I'm not 100% convinced it's the right thing to do either. Especially if we already have real_nth_root, then we have ^(1/n) for the complex root, and... presumably you know which one you want? I dunno. But until someone finds the existing methods lacking, adding them proactively just increases the chances that we'll have gotten it wrong when the need does arise.

comment:45 Changed 5 months ago by git

  • Commit changed from 00380b09135e8171593108059f17490a23f554ad to f8cb7a0429c2e696dc69db8466ecc077b0bf0f32

Branch pushed to git repo; I updated commit sha1. New commits:

f8cb7a0Refactored docs

comment:46 in reply to: ↑ 40 ; follow-up: Changed 5 months ago by klee

Replying to kcrisman:

However, that brings up the related issue that the online documentation is only going to show the _init_ method doc (and apparently not the tests?).

If you are up for it, I'd recommend making a more "narrative" structure which includes examples of calculus, plotting, etc., more like real_part does. If you'd prefer me to do that, I can - might just take a little longer as I recall how to add to a public branch :-)

Done.

  • input:
    sage: H = real_nth_root(x,2)
    sage: (H.subs(x=-1))^2
    ---------------------------------------------------------------------------
    ...
    ValueError: no real nth root of negative real number with even n
    sage: ((H)^2).subs(x=-1)
    1
    

    I'm not sure the latter should be allowed or not.

    No. real_nth_root takes real input and outputs real. For the symbolic function 1/x, we assume x takes nonzero value. Likewise, we assume real value x for real_nth_root(x, 3).

    Wait, so are you saying that we should disallow that function with x=-1 to be evaluated?

Yes. What should real_nth_root(-1, 2) evaluate to?

Sage simplifies 1/x*x =1 which isn't technically correct.

Why is it not correct? It is correct but you should not evaluate 1/x at x=0.

comment:47 in reply to: ↑ 46 ; follow-up: Changed 5 months ago by kcrisman

Done.

Thanks, I'll check that out momentarily to confirm it builds properly. Looks good so far.

  • input:
    sage: H = real_nth_root(x,2)
    sage: (H.subs(x=-1))^2
    ---------------------------------------------------------------------------
    ...
    ValueError: no real nth root of negative real number with even n
    sage: ((H)^2).subs(x=-1)
    1
    

    I'm not sure the latter should be allowed or not.

    No. real_nth_root takes real input and outputs real. For the symbolic function 1/x, we assume x takes nonzero value. Likewise, we assume real value x for real_nth_root(x, 3).

    Wait, so are you saying that we should disallow that function with x=-1 to be evaluated?

Yes. What should real_nth_root(-1, 2) evaluate to?

I meant ((real_nth_root(x, 2))^2).subs(x=-1). Are you saying this should be allowed or not allowed?

Sage simplifies 1/x*x =1 which isn't technically correct.

Why is it not correct? It is correct but you should not evaluate 1/x at x=0.

Depends on whether such expressions are meant to include domains - but anyway that is not directly relevant.

comment:48 in reply to: ↑ 47 ; follow-up: Changed 5 months ago by kcrisman

  • Reviewers set to Karl-Dieter Crisman, Nils Bruin, Kwanyku Lee

Thank you for all the good work - this is really long since overdue.

Yes. What should real_nth_root(-1, 2) evaluate to?

I meant ((real_nth_root(x, 2))^2).subs(x=-1). Are you saying this should be allowed or not allowed?

Again, see comment:21. It is good that real_nth_root(-1, 2) does not evaluate, but the question is whether the symbolic simplification should do this when ((real_nth_root(x, 2))^2) doesn't really make sense for x=-1. Here is another example.

sage: ((real_nth_root(x, 2))^2)
real_nth_root(x^2, 2)
sage: f(x) = _
sage: f
x |--> real_nth_root(x^2, 2)
sage: f(-1)
1

I think if in _power_ we had something like

if exp % 2 == 1:
    return self(base**power_param, exp)
else:
 ....

that might solve it (suitable tests included). What do you think? Unfortunately the most naive things I tried for the else clause led to nasty errors; I wasted about 20 minutes on abort errors, so I have a feeling that what I did leads to Pynac problems. (Anyone else interested in this particular question other than me or Kwankyu?)

Probably we don't need to assert that n is a positive integer since the documentation clearly says so and since why would anyone use it any other way? (Famous last words.)

comment:49 in reply to: ↑ 48 ; follow-up: Changed 5 months ago by klee

Replying to kcrisman:

Thank you for all the good work - this is really long since overdue.

Yes. What should real_nth_root(-1, 2) evaluate to?

I meant ((real_nth_root(x, 2))^2).subs(x=-1). Are you saying this should be allowed or not allowed?

Allowed. This is exactly the same problem with

sage: (1/x)*x
1
sage: _.subs(x=0)
1

which we don't bother to fix in any way. If you argue the cases are different because the difference of the domains of definition is of measure zero, then how about this:

sage: f = 1/floor(abs(x))*floor(abs(x))
sage: f.subs(x=1/2)
1

Perhaps it is important that these things do not cause real problems because the domain of definition never gets smaller from the initial domain of definition that one starts with.

comment:50 in reply to: ↑ 49 ; follow-up: Changed 5 months ago by kcrisman

  • Status changed from needs_work to positive_review

Allowed. This is exactly the same problem with

Okay, I'll roll with that. Haven't heard from any of the other people regarding that here, I don't have a big commitment here since this isn't really switchable to "regular" root functions. Thank you!

comment:51 in reply to: ↑ 50 Changed 5 months ago by klee

Thank you!

comment:52 Changed 5 months ago by egourgoulhon

Thank you very much for making this happen!

comment:53 Changed 5 months ago by vbraun

  • Branch changed from public/12074 to f8cb7a0429c2e696dc69db8466ecc077b0bf0f32
  • Resolution set to fixed
  • Status changed from positive_review to closed

comment:54 Changed 5 weeks ago by mkoeppe

  • Commit f8cb7a0429c2e696dc69db8466ecc077b0bf0f32 deleted
  • Reviewers changed from Karl-Dieter Crisman, Nils Bruin, Kwanyku Lee to Karl-Dieter Crisman, Nils Bruin, Kwankyu Lee
Note: See TracTickets for help on using tickets.