Re: [Python-ideas] Fix some special cases in Fractions?

On 2018-08-30 06:39, Neil Girdhar wrote:
I'd like these to be Fraction(1), Fraction(1), and Fraction(0).
Why? I cannot think of any natural use case why you would want Fractions for a few special cases on an operation which returns non-Fractions generically. I consider it a feature to know in advance the type of the output of an operation, given the types of the input. Having an unexpected type suddenly show up because you happen to hit a special case is a recipe for bugs. Jeroen.

Jeroen Demeyer wrote:
On 2018-08-30 06:39, Neil Girdhar wrote:
I'd like these to be Fraction(1), Fraction(1), and Fraction(0).
Why? I cannot think of any natural use case why you would want Fractions for a few special cases on an operation which returns non-Fractions generically.
Also, Fraction(1) for the second case would be flat-out wrong. -- Greg

On Thu, Aug 30, 2018, 2:08 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Also, Fraction(1) for the second case would be flat-out wrong.
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it. -1 squared is 1, and the cube root of 1 is 1. Or am I having a 2:30am brain fart?

On Thu, Aug 30, 2018 at 4:30 PM, Jonathan Goble <jcgoble3@gmail.com> wrote:
On Thu, Aug 30, 2018, 2:08 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Also, Fraction(1) for the second case would be flat-out wrong.
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it. -1 squared is 1, and the cube root of 1 is 1. Or am I having a 2:30am brain fart?
For positive numbers, I believe you're correct. For negative numbers, no.
(-2)**(2/3) (-0.7937005259840993+1.3747296369986026j) _**(3/2) (-1.9999999999999993+2.4492935982947054e-16j) ((-2)**2)**(1/3) 1.5874010519681994 _**(3/2) 1.9999999999999998
Rounding error aside, raising -2 to 2/3 power and then raising the result to 3/2 power gives back -2, as it should. Doing it in two steps loses the negation, and then (again, within rounding error) the end result is positive two. ChrisA

Jonathan Goble wrote:
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it.
On reflection, "wrong" is not quite accurate. A better word might be "surprising". (-1) ** (2/3) == 1 would imply that 1 ** (3/2) == -1. I suppose that could be considered true if you take the negative solution of the square root, but it seems a bit strange, and it's not what Python gives you for the result of 1 ** (3/2). If you want a solution that round-trips, you need complex numbers. That's what Python does when you use floats. Making Fractions do something different would make it inconsistent with floats. My calculator (which only does real floats) reports an error when trying to evaluate (-1) ** (2/3). -- Greg

There are a lot of misunderstandings in this thread. It's probably best to start by reading up on the roots of unity ( https://en.wikipedia.org/wiki/Root_of_unity). The key ideas are that a real number has two complex square roots, three complex cube roots, and so on. Normally, in Python, we return the principal square root ( http://mathworld.wolfram.com/PrincipalSquareRoot.html). We would do the same for the cube root I imagine if we had a cube root function Let's call that the principal cube root, which is always real for a real-valued input. ---
For positive numbers, I believe you're correct. For negative numbers, no.
Actually, Jonathan is right.
(-2)**(2/3) (-0.7937005259840993+1.3747296369986026j)
This is just giving you one of the other two cube roots.
Rounding error aside, raising -2 to 2/3 power and then raising the result to 3/2 power gives back -2, as it should.
There is no way to guarantee that "it should do this". A fractional power is not a function, and so it has no inverse function.
Doing it in two steps loses the negation, and then (again, within rounding error) the end result is positive two.
I think you would see your mistake if you applied this logic to -1 ** 2 ** (1/2). On Thu, Aug 30, 2018 at 3:08 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Jonathan Goble wrote:
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it.
On reflection, "wrong" is not quite accurate. A better word might be "surprising".
(-1) ** (2/3) == 1 would imply that 1 ** (3/2) == -1.
I suppose that could be considered true if you take the
negative solution of the square root, but it seems a bit strange, and it's not what Python gives you for the result of 1 ** (3/2).
Python gives you the principle root when you do 1 ** (3/2), which is exactly what I'm proposing for Fraction.
If you want a solution that round-trips, you need complex numbers.
There is no solution that round trips since in general fractional powers are not functions. The case in which you can always round trip is when you are taking a power c ** (a/b) where a is odd, and either c is positive or b is odd. Then, the result to the power of (b/a) gives you c. That's what Python does when you use
floats. Making Fractions do something different would make it inconsistent with floats.
Actually, my cases are all examples of the principal roots and are consistent with floats.
My calculator (which only does real floats) reports an error when trying to evaluate (-1) ** (2/3).
Yes, which is what Python does with reals. The difference is the the Fraction type has exact values, and it knows that the unique answer in the field of Fraction objects is 1.
-- Greg
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/aZIHpPhe0mw/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.

On Thu, 30 Aug 2018 at 08:38, Neil Girdhar <mistersheik@gmail.com> wrote:
There are a lot of misunderstandings in this thread. It's probably best to start by reading up on the roots of unity (https://en.wikipedia.org/wiki/Root_of_unity). The key ideas are that a real number has two complex square roots, three complex cube roots, and so on.
The complexities of fractional powers aside, the type of a result should not depend on the values of the arguments. So I'm -1 on this change for that reason alone. Questions of which root it's appropriate to take are separate, and IMO the sensible option is to follow the behaviour of float, for which we have
(-1)**(2/3) (-0.4999999999999998+0.8660254037844387j) (1)**(2/3) 1.0 (0)**(2/3) 0.0
So current behaviour of Fraction is correct on that basis, IMO. Paul

Sorry if this gets double posted. Can people using Google Groups *please* adjust the mail headers so that mailing list posters can reply without getting errors? Ideally stop using Google Groups, but if you have to, please consider those that don't. Specifically, please remove the Google Groups address from the reply-to header, so that replies go direct to the mailing list and not to the Google Groups address :-( (Google Groups messages also don't filter properly, so I end up also having to manually re-file messages that come via that route. While I doubt GG users can do much about that, it is another downside of using the GG interface, and one that GG users should be aware of). Sorry, feeling grumpy about GG today. On Thu, 30 Aug 2018 at 09:01, Paul Moore <p.f.moore@gmail.com> wrote:
On Thu, 30 Aug 2018 at 08:38, Neil Girdhar <mistersheik@gmail.com> wrote:
There are a lot of misunderstandings in this thread. It's probably best to start by reading up on the roots of unity (https://en.wikipedia.org/wiki/Root_of_unity). The key ideas are that a real number has two complex square roots, three complex cube roots, and so on.
The complexities of fractional powers aside, the type of a result should not depend on the values of the arguments. So I'm -1 on this change for that reason alone.
Questions of which root it's appropriate to take are separate, and IMO the sensible option is to follow the behaviour of float, for which we have
(-1)**(2/3) (-0.4999999999999998+0.8660254037844387j) (1)**(2/3) 1.0 (0)**(2/3) 0.0
So current behaviour of Fraction is correct on that basis, IMO.
Paul

On Thu, Aug 30, 2018 at 4:01 AM Paul Moore <p.f.moore@gmail.com> wrote:
On Thu, 30 Aug 2018 at 08:38, Neil Girdhar <mistersheik@gmail.com> wrote:
There are a lot of misunderstandings in this thread. It's probably best
to start by reading up on the roots of unity ( https://en.wikipedia.org/wiki/Root_of_unity). The key ideas are that a real number has two complex square roots, three complex cube roots, and so on.
The complexities of fractional powers aside, the type of a result should not depend on the values of the arguments. So I'm -1 on this change for that reason alone.
Just so you know, it already does depend on the type of the arguments. In [4]: Fraction(2) ** Fraction(1, 2) Out[4]: 1.4142135623730951 In [5]: Fraction(2) ** Fraction(3, 1) Out[5]: Fraction(8, 1) Like a lot of types, Fraction tries to return a Fraction if it can. This is consistent with how math.sqrt of a float returns a float and never a complex, and same for numpy.cbrt. Questions of which root it's appropriate to take are separate, and IMO
the sensible option is to follow the behaviour of float, for which we have
(-1)**(2/3) (-0.4999999999999998+0.8660254037844387j)
But -1 ** Fraction(2, 3) is closer to cube_root(-1 ** 2) than it is to the floating behavior. The reason the floating behavior returns what it does is, if I understand correctly, to try to stay on the same complex branch as the power varies. In other words, we want branch continuity in the power. After all, floating point values have some inaccuracy, and we wouldn't want chaotic behavior, i.e., small changes to the power to have drastic changes to the result. This is not like Fraction where we know that x ** Fraction(1, 3) is a genuine cube root, and so why not return the principal cube, which we know to be real valued for real valued x?
(1)**(2/3) 1.0
(0)**(2/3) 0.0
So current behaviour of Fraction is correct on that basis, IMO.
Paul

Neil Girdhar wrote:
we want branch continuity in the power. After all, floating point values have some inaccuracy, and we wouldn't want chaotic behavior, i.e., small changes to the power to have drastic changes to the result.
This is not like Fraction where we know that x ** Fraction(1, 3) is a genuine cube root, and so why not return the principal cube, which we know to be real valued for real valued x?
Because that would be possible only for a few special combinations of Fractions ** Fractions that happen to have rational solutions. All the others would still have to return float or complex results, which could then be discontinuous with the rational ones. -- Greg

On Thu, Aug 30, 2018 at 5:27 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Neil Girdhar wrote:
we want branch continuity in the power. After all, floating point values have some inaccuracy, and we wouldn't want chaotic behavior, i.e., small changes to the power to have drastic changes to the result.
This is not like Fraction where we know that x ** Fraction(1, 3) is a genuine cube root, and so why not return the principal cube, which we know to be real valued for real valued x?
Because that would be possible only for a few special combinations of Fractions ** Fractions that happen to have rational solutions. All the others would still have to return float or complex results, which could then be discontinuous with the rational ones.
Right, but we already have some special cases:
In [8]: Fraction(2, 3) ** Fraction(3, 1) Out[8]: Fraction(8, 27) Fraction.__pow__ already tries to return Fraction objects where possible. --
Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/aZIHpPhe0mw/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.

Right, but we already have some special cases:
In [8]: Fraction(2, 3) ** Fraction(3, 1) Out[8]: Fraction(8, 27)
Fraction.__pow__ already tries to return Fraction objects where possible.
I think the main point to see here is what the scope of a built-in function should be. For a fraction module in the stdlib, I would expect that it handle "symbolically" any fraction multiplication or division of fractions, and integer power of fractions. Those are simple and useful cases, that can arise a bit anywhere. Power of non-integer is a way more complex issue (notably because power of a non-integer is not a function), and returning the same output as float is at least an honest way of dealing with those cases. I'm not really sure a stdlib should even try do deal with that. If I want to have a symbolic way of handling complex power of fractions, I should import a specific math library whose specific job is to get this right (the same way if you want to do matrix stuff you have to import numpy). -- *Nicolas Rolin*

On Thu, Aug 30, 2018 at 5:51 AM Nicolas Rolin <nicolas.rolin@tiime.fr> wrote:
Right, but we already have some special cases:
In [8]: Fraction(2, 3) ** Fraction(3, 1) Out[8]: Fraction(8, 27)
Fraction.__pow__ already tries to return Fraction objects where possible.
I think the main point to see here is what the scope of a built-in function should be. For a fraction module in the stdlib, I would expect that it handle "symbolically" any fraction multiplication or division of fractions, and integer power of fractions. Those are simple and useful cases, that can arise a bit anywhere. Power of non-integer is a way more complex issue (notably because power of a non-integer is not a function), and returning the same output as float is at least an honest way of dealing with those cases.
But I'm only asking for fractional powers of -1, 0, and 1. Is that really a complex issue? You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
I'm not really sure a stdlib should even try do deal with that. If I want to have a symbolic way of handling complex power of fractions, I should import a specific math library whose specific job is to get this right (the same way if you want to do matrix stuff you have to import numpy).
That's how I use the fractions package. If you look at my example code, that seems like the kind of problem Fraction should make it easy to work with. And yet, there was a wrinkle where I had to calculate Fraction(-1) ** Fraction(a, b).
--
*Nicolas Rolin*
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/aZIHpPhe0mw/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/aZIHpPhe0mw/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.

Hi Neil Summary: You say something should be done. But by who? Perhaps the should starts with you. Warning: This has been written quickly, and might have rough edges. If so, I apologise. You wrote
But I'm only asking for fractional powers of -1, 0, and 1. Is that really a complex issue?
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
I'm not really sure a stdlib should even try do deal with that. If I want to have a symbolic way of handling complex power of fractions, I should import a specific math library whose specific job is to get this right (the same way if you want to do matrix stuff you have to import numpy).
That's how I use the fractions package. If you look at my example code, that seems like the kind of problem Fraction should make it easy to work with. And yet, there was a wrinkle where I had to calculate Fraction(-1) ** Fraction(a, b).
Let's try to make the best of what we've got. First, the docs for the fraction module could be improved. Here's the page and it's history. https://docs.python.org/3/library/fractions.html https://github.com/python/cpython/commits/3.7/Doc/library/fractions.rst Already, doing this will help users. In particular, for a different approach, point them to https://www.sympy.org/en/index.html Second, assume you're right, when you say
[This sort of problem], Fraction should make it easy [...]
The problem is, Python development resources are finite. They should be more, but they're not. It's hard to go from 'Fraction should' to '<specific individuals> should'. Perhaps the best one can come up with is the developers of fraction.py. Here's the page and history. https://github.com/python/cpython/blob/3.7/Lib/fractions.py https://github.com/python/cpython/commits/3.7/Lib/fractions.py Third, there is a way to go from 'Fraction should' to a group of persons. If you're correct, that 'Fraction should', then I'm happy to say that it follows that 'the Python community should'. Now, the Python community is vast. It's more than the core developers. I'm a member. You're a member. Everyone participating on this list is a member. It's got lots of resources. You want this problem solved. You've got clear ideas what should be done. You say it should be done. And you're a member of the Python community, who 'should fix this problem'. The module fractions.py is pure Python. You can fork it to develop your own solution, written as it should be. You can use this solution and share it with the community. And you can submit it as a pull request, for inclusion in the standard library. To summarise. You're part of the Python community, who you believe should solve this problem. Please, as a member of the community, accept some responsibility for do what you think should be done. And finally, thank you for drawing to our attention this blemish in the fractions module (or its documentation). -- Jonathan

On Thu, Aug 30, 2018 at 7:03 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Hi Neil
Summary: You say something should be done. But by who? Perhaps the should starts with you.
What does this mean? Are you asking who is going to do the implementation? I posted here to get feedback about whether it would be a good idea.
Warning: This has been written quickly, and might have rough edges. If so, I apologise.
You wrote
But I'm only asking for fractional powers of -1, 0, and 1. Is that really a complex issue?
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
I'm not really sure a stdlib should even try do deal with that. If I want to have a symbolic way of handling complex power of fractions, I should import a specific math library whose specific job is to get this right (the same way if you want to do matrix stuff you have to import numpy).
That's how I use the fractions package. If you look at my example code, that seems like the kind of problem Fraction should make it easy to work with. And yet, there was a wrinkle where I had to calculate Fraction(-1) ** Fraction(a, b).
Let's try to make the best of what we've got.
First, the docs for the fraction module could be improved. Here's the page and it's history.
https://docs.python.org/3/library/fractions.html
https://github.com/python/cpython/commits/3.7/Doc/library/fractions.rst
Already, doing this will help users. In particular, for a different approach, point them to
https://www.sympy.org/en/index.html
Second, assume you're right, when you say
[This sort of problem], Fraction should make it easy [...]
The problem is, Python development resources are finite. They should be more, but they're not. It's hard to go from 'Fraction should' to '<specific individuals> should'. Perhaps the best one can come up with is the developers of fraction.py. Here's the page and history.
https://github.com/python/cpython/blob/3.7/Lib/fractions.py https://github.com/python/cpython/commits/3.7/Lib/fractions.py
Thanks for linking the github. I'm only posting on ideas to get feedback. I think this is what the group is for. Also, I don't know why you keep bringing up the finite Python developer resources. Like I said above, this is a small change, (one that I coded below). Besides that bit of code, I could add a few tests, and modify the docs. Someone would have to review it, but it's not a big job. The only question is: should it be done? And that's why I posted it here.
Third, there is a way to go from 'Fraction should' to a group of persons. If you're correct, that 'Fraction should', then I'm happy to say that it follows that 'the Python community should'.
Now, the Python community is vast. It's more than the core developers. I'm a member. You're a member. Everyone participating on this list is a member. It's got lots of resources.
You want this problem solved. You've got clear ideas what should be done. You say it should be done. And you're a member of the Python community, who 'should fix this problem'.
The module fractions.py is pure Python. You can fork it to develop your own solution, written as it should be. You can use this solution and share it with the community. And you can submit it as a pull request, for inclusion in the standard library.
Before I do that, I want to get feedback here. The way I understand the usual order with feature requests is that they are discussed, they turn into PEPs, they are approved, they are implemented, they are reviewed, they are checked in. Someone will definitely correct me. No one wants to spend time writing documentation for code that will never be checked in.
To summarise. You're part of the Python community, who you believe should solve this problem. Please, as a member of the community, accept some responsibility for do what you think should be done.
Maybe I'm interpreting this wrong, but what do you mean by "accept some responsibility"? The last time I felt strongly about a feature (PEP 448), and after it had been approved by Guido, I spent a few months implementing it along with Joshua Landau (https://bugs.python.org/issue2292). Compared to this tiny change, that was a huge change involving the parser, the compiler, and the grammar. I really don't understand what you're driving at with this "accept some responsibility" comment.
And finally, thank you for drawing to our attention this blemish in
the fractions module (or its documentation).
-- Jonathan

Hi Neil When I wrote my previous message, I didn't know who you were, or your previous contributions. Perhaps I should have. But I didn't. If I had known, my remarks would have been different. In particular, I would have acknowledged your previous contributions. I apologise for any offence I may have caused you. Better late than never. Thank you for your contributions to implementing PEP 448. with best regards Jonathan

Hey, no worries. I do think though that people should feel free to suggest ideas even if they have never contributed anything. I read python-ideas for the discussion. Thank you for your feedback about my suggestion. On Thu, Aug 30, 2018 at 8:18 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Hi Neil
When I wrote my previous message, I didn't know who you were, or your previous contributions. Perhaps I should have. But I didn't.
If I had known, my remarks would have been different. In particular, I would have acknowledged your previous contributions. I apologise for any offence I may have caused you.
Better late than never. Thank you for your contributions to implementing PEP 448.
with best regards
Jonathan

On Thu, 30 Aug 2018 at 12:04, Jonathan Fine <jfine2358@gmail.com> wrote:
First, the docs for the fraction module could be improved. Here's the page and it's history.
https://docs.python.org/3/library/fractions.html https://github.com/python/cpython/commits/3.7/Doc/library/fractions.rst
Already, doing this will help users.
While the documentation is pretty terse and could be expanded, it's worth noting that the class also has docstrings. And whether we feel that docstrings are sufficiently accessible for users (certainly I often forget to check them) they *are* part of the module documentation. In particular:
help(fractions.Fraction.__pow__) Help on function __pow__ in module fractions:
__pow__(a, b) a ** b If b is not an integer, the result will be a float or complex since roots are generally irrational. If b is an integer, the result will be rational. That's clear, concise, and explains the current behaviour precisely. Neil's proposal is definitely a functional change (not that he was denying this) in that it alters already documented behaviour. And I think that's something we should remember. This isn't undocumented (or even badly documented) behaviour - it's clearly defined behaviour noted in the official documentation (just in a part of that documentation that's easy to overlook).
In particular, for a different approach, point them to
I don't honestly think that sympy is a comparable tool to the stdlib Fraction class. I suspect that most people who need the Fraction class are not even remotely in the target audience for sympy. The ones who are, likely already know about it.
And finally, thank you for drawing to our attention this blemish in the fractions module (or its documentation).
I wouldn't describe it as a blemish. Rather, it's somewhere we could maybe improve the discoverability of the existing documentation. Or an opportunity to review how we make it easier for users to get access to documentation that is held in docstrings, as opposed to the manuals. Maybe a pydoc instance available online, linked from the manual entries? Paul

(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so). On Thu, 30 Aug 2018 at 11:28, Neil Girdhar <mistersheik@gmail.com> wrote:
But I'm only asking for fractional powers of -1, 0, and 1. Is that really a complex issue?
Yes. (Even ignoring the oh-so-tempting complex number joke ;-)). As has been seen here there's no agreement on the "right" choice of which root of -1 to choose. Or possibly more accurately, no-one else is agreeing with your suggestion that we choose a different option for the case you're arguing over. And to be 100% precise, you asked for the results of three *very specific* calculations to change. I guess you actually want something more general - or are you really OK with (for example) Fraction(-1,1)**Fraction(2,3) changing as you request, but Fraction(-2,1)**Fraction(2,3) remaining as it currently is? You still haven't clarified (no-one has particularly asked yet - you may consider this a request to do so if you like) how you propose in general that the result of Fraction(-1,1) ** Fraction(a, b) and/or Fraction(1,1) ** Fraction(a, b) or maybe even more generally Fraction(c,d) ** Fraction(a,b) would change. What exactly are the special cases you want to define different results for? What is the process for choosing the result?
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
And that part of your proposal has not generated much controversy. Maybe if you proposed only that, you might get that change made? I haven't considered the ramifications of that because the discussions about -1 are obscuring it, but it might be relatively uncontroversial. Paul

Thanks for the feedback. On Thu, Aug 30, 2018 at 7:13 AM Paul Moore <p.f.moore@gmail.com> wrote:
(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so).
On Thu, 30 Aug 2018 at 11:28, Neil Girdhar <mistersheik@gmail.com> wrote:
But I'm only asking for fractional powers of -1, 0, and 1. Is that
really a complex issue?
Yes. (Even ignoring the oh-so-tempting complex number joke ;-)). As has been seen here there's no agreement on the "right" choice of which root of -1 to choose. Or possibly more accurately, no-one else is agreeing with your suggestion that we choose a different option for the case you're arguing over.
And to be 100% precise, you asked for the results of three *very specific* calculations to change. I guess you actually want something more general - or are you really OK with (for example) Fraction(-1,1)**Fraction(2,3) changing as you request, but Fraction(-2,1)**Fraction(2,3) remaining as it currently is?
Powers of other numbers have to keep the same behavior since in general those kinds of expressions don't create rational numbers.
You still haven't clarified (no-one has particularly asked yet - you may consider this a request to do so if you like) how you propose in general that the result of
Fraction(-1,1) ** Fraction(a, b) and/or Fraction(1,1) ** Fraction(a, b) or maybe even more generally Fraction(c,d) ** Fraction(a,b)
would change. What exactly are the special cases you want to define different results for? What is the process for choosing the result?
Here's my proposed method: class Fraction: def __pow__(a, b): """a ** b If b is not an integer, the result will be a float or complex since roots are generally irrational. If b is an integer, the result will be rational. """ if isinstance(b, numbers.Rational): if b.denominator == 1: power = b.numerator if power >= 0: return Fraction(a._numerator ** power, a._denominator ** power, _normalize=False) elif a._numerator >= 0: return Fraction(a._denominator ** -power, a._numerator ** -power, _normalize=False) else: return Fraction((-a._denominator) ** -power, (-a._numerator) ** -power, _normalize=False) elif a == -1 and b.denominator % 2 == 1: return Fraction(-1 if b.numerator % 2 == 1 else 1) elif a == 0: if b > 0: return Fraction(0) else: raise ZeroDivisionError( "0 cannot be raised to a negative power") elif a == 1: return Fraction(1) else: # A fractional power will generally produce an # irrational number. return float(a) ** float(b) else: return float(a) ** b Compare it with https://github.com/python/cpython/blob/3.7/Lib/fractions.py#L448
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
And that part of your proposal has not generated much controversy. Maybe if you proposed only that, you might get that change made? I haven't considered the ramifications of that because the discussions about -1 are obscuring it, but it might be relatively uncontroversial.
Fair enough.
Paul

I think you could take the implementation further and decide that any power of Fraction(1) is Fraction(1) and any positive power of Fraction(0) is Fraction(0). I woudn't be shocked that Fraction(1) ** 3.7 == Fraction(1) and Fraction(0) ** 3.7 == 0. However the implementation for Fraction(-1) seems a bit to "ad hoc", and break some expected behavior of the powers. For exemple in your code Fraction(-2) ** Fraction(2, 3) != Fraction(-1) ** Fraction(2, 3) * Fraction(2) ** Fraction(2, 3), whereas floats respect this. You could change the code so that the property (a *b) ** c == a**c *b**c, but idk how hard it is. 2018-08-30 13:31 GMT+02:00 Neil Girdhar <mistersheik@gmail.com>:
Thanks for the feedback.
On Thu, Aug 30, 2018 at 7:13 AM Paul Moore <p.f.moore@gmail.com> wrote:
(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so).
On Thu, 30 Aug 2018 at 11:28, Neil Girdhar <mistersheik@gmail.com> wrote:
But I'm only asking for fractional powers of -1, 0, and 1. Is that
really a complex issue?
Yes. (Even ignoring the oh-so-tempting complex number joke ;-)). As has been seen here there's no agreement on the "right" choice of which root of -1 to choose. Or possibly more accurately, no-one else is agreeing with your suggestion that we choose a different option for the case you're arguing over.
And to be 100% precise, you asked for the results of three *very specific* calculations to change. I guess you actually want something more general - or are you really OK with (for example) Fraction(-1,1)**Fraction(2,3) changing as you request, but Fraction(-2,1)**Fraction(2,3) remaining as it currently is?
Powers of other numbers have to keep the same behavior since in general those kinds of expressions don't create rational numbers.
You still haven't clarified (no-one has particularly asked yet - you may consider this a request to do so if you like) how you propose in general that the result of
Fraction(-1,1) ** Fraction(a, b) and/or Fraction(1,1) ** Fraction(a, b) or maybe even more generally Fraction(c,d) ** Fraction(a,b)
would change. What exactly are the special cases you want to define different results for? What is the process for choosing the result?
Here's my proposed method:
class Fraction: def __pow__(a, b): """a ** b If b is not an integer, the result will be a float or complex since roots are generally irrational. If b is an integer, the result will be rational. """ if isinstance(b, numbers.Rational): if b.denominator == 1: power = b.numerator if power >= 0: return Fraction(a._numerator ** power, a._denominator ** power, _normalize=False) elif a._numerator >= 0: return Fraction(a._denominator ** -power, a._numerator ** -power, _normalize=False) else: return Fraction((-a._denominator) ** -power, (-a._numerator) ** -power, _normalize=False) elif a == -1 and b.denominator % 2 == 1: return Fraction(-1 if b.numerator % 2 == 1 else 1) elif a == 0: if b > 0: return Fraction(0) else: raise ZeroDivisionError( "0 cannot be raised to a negative power") elif a == 1: return Fraction(1) else: # A fractional power will generally produce an # irrational number. return float(a) ** float(b) else: return float(a) ** b
Compare it with https://github.com/python/cpython/blob/3.7/Lib/ fractions.py#L448
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
And that part of your proposal has not generated much controversy. Maybe if you proposed only that, you might get that change made? I haven't considered the ramifications of that because the discussions about -1 are obscuring it, but it might be relatively uncontroversial.
Fair enough.
Paul
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- -- *Nicolas Rolin* | Data Scientist + 33 631992617 - nicolas.rolin@tiime.fr <prenom.nom@tiime.fr> *15 rue Auber, **75009 Paris* *www.tiime.fr <http://www.tiime.fr>*

On Thu, 30 Aug 2018 at 14:06, Nicolas Rolin <nicolas.rolin@tiime.fr> wrote:
I think you could take the implementation further and decide that any power of Fraction(1) is Fraction(1) and any positive power of Fraction(0) is Fraction(0). I woudn't be shocked that Fraction(1) ** 3.7 == Fraction(1) and Fraction(0) ** 3.7 == 0.
You could do lots of things. But are any of them useful enough to warrant changing documented behaviour, breaking backward compatibility, and introducing a much more complex rule on how the arguments to Fraction.__pow__ affect the type of the return value? At the moment, there are no known use cases for this change - beyond Neil's statement that he'd "like" the values to be as he quoted (with no justification given).
However the implementation for Fraction(-1) seems a bit to "ad hoc", and break some expected behavior of the powers. For exemple in your code Fraction(-2) ** Fraction(2, 3) != Fraction(-1) ** Fraction(2, 3) * Fraction(2) ** Fraction(2, 3), whereas floats respect this. You could change the code so that the property (a *b) ** c == a**c *b**c, but idk how hard it is.
This (what behaviour is "expected" and/or natural) is the sort of detail that can't be answered without knowing use cases - both use cases for the proposed behaviour *and* use cases that rely on the existing behaviour. Paul

Neil Girdhar wrote:
Powers of other numbers have to keep the same behavior since in general those kinds of expressions don't create rational numbers.
There are infinitely many other rational numbers that *could* be given the same treatment, though, e.g. (-8) ** (2/3). If you don't want to include those you'll have to explain your rationale. -- Greg

On Thu, Aug 30, 2018 at 7:13 AM Paul Moore <p.f.moore@gmail.com> wrote:
(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so).
Sorry about that, I don't understand where it's coming from. I'm never using google groups. I'm only replying to the emails that are sent to me. I guess gmail's reply-all is gathering the google groups mail from the thread.
On Thu, 30 Aug 2018 at 11:28, Neil Girdhar <mistersheik@gmail.com> wrote:
But I'm only asking for fractional powers of -1, 0, and 1. Is that
really a complex issue?
Yes. (Even ignoring the oh-so-tempting complex number joke ;-)). As has been seen here there's no agreement on the "right" choice of which root of -1 to choose. Or possibly more accurately, no-one else is agreeing with your suggestion that we choose a different option for the case you're arguing over.
And to be 100% precise, you asked for the results of three *very specific* calculations to change. I guess you actually want something more general - or are you really OK with (for example) Fraction(-1,1)**Fraction(2,3) changing as you request, but Fraction(-2,1)**Fraction(2,3) remaining as it currently is? You still haven't clarified (no-one has particularly asked yet - you may consider this a request to do so if you like) how you propose in general that the result of
Fraction(-1,1) ** Fraction(a, b) and/or Fraction(1,1) ** Fraction(a, b) or maybe even more generally Fraction(c,d) ** Fraction(a,b)
would change. What exactly are the special cases you want to define different results for? What is the process for choosing the result?
You are right that the fractional power of -1 and 1 has multiple values, but the fractional power of zero has a unique value.
And that part of your proposal has not generated much controversy. Maybe if you proposed only that, you might get that change made? I haven't considered the ramifications of that because the discussions about -1 are obscuring it, but it might be relatively uncontroversial.
Paul

Thu, 30 Aug 2018 at 12:55, Neil Girdhar <mistersheik@gmail.com> wrote:
On Thu, Aug 30, 2018 at 7:13 AM Paul Moore <p.f.moore@gmail.com> wrote:
(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so).
Sorry about that, I don't understand where it's coming from. I'm never using google groups. I'm only replying to the emails that are sent to me. I guess gmail's reply-all is gathering the google groups mail from the thread.
Your original mail was sent to Google Groups. Do you have the wrong address for the list in your mail client? Message ID<0067a655-4f82-479f-9970-5b72dc079364@googlegroups.com> Created on:30 August 2018 at 05:39 (Delivered after 74 seconds) From:Neil Girdhar <mistersheik@gmail.com> To:python-ideas <python-ideas@googlegroups.com> Subject:[Python-ideas] Fix some special cases in Fractions? Paul

Yeah, you're right, my original mail was posted on google groups. Sorry for the trouble. On Thu, Aug 30, 2018 at 8:15 AM Paul Moore <p.f.moore@gmail.com> wrote:
Thu, 30 Aug 2018 at 12:55, Neil Girdhar <mistersheik@gmail.com> wrote:
On Thu, Aug 30, 2018 at 7:13 AM Paul Moore <p.f.moore@gmail.com> wrote:
(You're still not fixing your mail headers. Please do, it's hard to be bothered responding if I keep having to fix your mails in order to do so).
Sorry about that, I don't understand where it's coming from. I'm never
using google groups. I'm only replying to the emails that are sent to me. I guess gmail's reply-all is gathering the google groups mail from the thread.
Your original mail was sent to Google Groups. Do you have the wrong address for the list in your mail client?
Message ID<0067a655-4f82-479f-9970-5b72dc079364@googlegroups.com> Created on:30 August 2018 at 05:39 (Delivered after 74 seconds) From:Neil Girdhar <mistersheik@gmail.com> To:python-ideas <python-ideas@googlegroups.com> Subject:[Python-ideas] Fix some special cases in Fractions?
Paul

Neil Girdhar writes:
There are a lot of misunderstandings in this thread. It's probably best to start by reading up on the roots of unity ( https://en.wikipedia.org/wiki/Root_of_unity).
That's not very polite, especially in context where somebody has already conceded that his "wrong" was over the top. Speaking of courtesy, please stop clean up your addressee list. If you don't, GoogleGroups spams people who reply with "you're not a member".
same for the cube root I imagine if we had a cube root function Let's call that the principal cube root, which is always real for a real-valued input.
That doesn't seem to be how Wolfram sees it: http://mathworld.wolfram.com/PrincipalRootofUnity.html

On Thu, Aug 30, 2018 at 4:38 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Neil Girdhar writes:
There are a lot of misunderstandings in this thread. It's probably best to start by reading up on the roots of unity ( https://en.wikipedia.org/wiki/Root_of_unity).
That's not very polite, especially in context where somebody has already conceded that his "wrong" was over the top.
Speaking of courtesy, please stop clean up your addressee list. If you don't, GoogleGroups spams people who reply with "you're not a member".
same for the cube root I imagine if we had a cube root function Let's call that the principal cube root, which is always real for a real-valued input.
That doesn't seem to be how Wolfram sees it:
Right, I meant real root. It just happens the principal square root is the real root.

On 2018-08-30 00:07, Greg Ewing wrote:> Jonathan Goble wrote:
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it.
On reflection, "wrong" is not quite accurate. A better word might be "surprising".
(-1) ** (2/3) == 1 would imply that 1 ** (3/2) == -1. I suppose that could be considered true if you take the negative solution of the square root, but it seems a bit strange, and it's not what Python gives you for the result of 1 ** (3/2).
If you want a solution that round-trips, you need complex numbers. That's what Python does when you use floats. Making Fractions do something different would make it inconsistent with floats.
My calculator (which only does real floats) reports an error when trying to evaluate (-1) ** (2/3).
The problem is that in the minds of most people who know enough math to know about fractional exponents, but still don't enough to want to deal with complex numbers, the standard procedure when taking a fractional power of a real number is: 1) choose a positive real value if there is one 2) choose a negative real value otherwise 3) give up if the answer would be nonreal But if you're willing to go to complex numbers, then the logic shifts to "take the principal root", which is the root in the complex plane that you get to first when going in the mathematically positive direction (counterclockwise) from the positive real axis. In addition, I think having 2 in the numerator in the fractional power is a red herring as far as understanding the confusion. This is already going to be surprising to people:
(-1) ** (1/3) (0.5000000000000001+0.8660254037844386j)
In high school math, people are taught that (-1)^(1/3) = -1, because that's the only real value. But if you open up to the complex numbers, then you'll start defining (-1)^(1/3) as (-1 + sqrt(3)i)/2 since that is the more mathematically defensible principal value. (Interestingly, Wolfram Alpha by default gives -1 for "cube root of -1", but gives the complex value for "(-1)^(1/3)". If only we had a way to type an nth-root symbol instead of having to indicate roots with exponents!) Personally I think I was happier with the way things worked in Python 2, where (-1)**(1/3) would raise an error, and you had to explicitly do (-1 + 0j)**(1/3) if you wanted a complex root. I'm willing to bet that the vast majority of users doing arithmetic with Python never want nor can make any use of a complex value for any operation, ever; it is more likely they want an error message to alert them that their data has gone awry. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Greg Ewing and Jonathan Goble wrote
Also, Fraction(1) for the second case would be flat-out wrong.
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it. -1 squared is 1, and the cube root of 1 is 1. Or am I having a 2:30am brain fart?
Let's see. What about computing the Fraction(2, 4) power by first squaring and then taking the fourth root. Let's start with (-16). Square to get +256. And then the fourth root is +4. I've just followed process Jonathan G suggested, without noticing that Fraction(2, 4) is equal to Fraction(1, 2). But Fraction(1, 2) is the square root. And -16 requires complex numbers for its square root. The problem, I think, may not be doing something sensible in any particular case. Rather, it could be doing something sensible and coherent in all cases. A bit like trying to fit a carpet that is cut to the wrong size for the room. -- Jonathan

If we wanted to be mathematically correct, taking a 4th root should give you 4 answers. You could return a tuple of (4, -4, 4j, -4j) for a 4th root of 256. It actually makes power to a Fraction(2, 4) unequal with a Fraction(1, 2) calculating this way. (which, from what I can tell, is exactly your point - don't just take a power and a square root for a fractional power, reduce it to a float or whatever first to get well-defined behaviour. ) But full mathematical correctness is probably not what we want either. Current behaviour IMO is the only solution that returns only 1 answer but does something sensible in all cases. Op za 1 sep. 2018 om 20:35 schreef Jonathan Fine <jfine2358@gmail.com>:
Greg Ewing and Jonathan Goble wrote
Also, Fraction(1) for the second case would be flat-out wrong.
How? Raising something to the 2/3 power means squaring it and then taking the cube root of it. -1 squared is 1, and the cube root of 1 is 1. Or am I having a 2:30am brain fart?
Let's see. What about computing the Fraction(2, 4) power by first squaring and then taking the fourth root. Let's start with (-16). Square to get +256. And then the fourth root is +4. I've just followed process Jonathan G suggested, without noticing that Fraction(2, 4) is equal to Fraction(1, 2).
But Fraction(1, 2) is the square root. And -16 requires complex numbers for its square root. The problem, I think, may not be doing something sensible in any particular case. Rather, it could be doing something sensible and coherent in all cases. A bit like trying to fit a carpet that is cut to the wrong size for the room.
-- Jonathan _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Thu, Aug 30, 2018 at 2:09 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Jeroen Demeyer wrote:
On 2018-08-30 06:39, Neil Girdhar wrote:
I'd like these to be Fraction(1), Fraction(1), and Fraction(0).
Why? I cannot think of any natural use case why you would want Fractions for a few special cases on an operation which returns non-Fractions generically.
Also, Fraction(1) for the second case would be flat-out wrong.
No, it's not "wrong". 1 is one of the cube roots of 1. It's consistent with 1 ** (2/3) == 1. --
Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/aZIHpPhe0mw/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
participants (11)
-
Brendan Barnwell
-
Chris Angelico
-
Greg Ewing
-
Jacco van Dorp
-
Jeroen Demeyer
-
Jonathan Fine
-
Jonathan Goble
-
Neil Girdhar
-
Nicolas Rolin
-
Paul Moore
-
Stephen J. Turnbull