Opened 11 years ago

Closed 11 years ago

#11779 closed defect (fixed)

python ints vs sage ints with respect to powers weirdness

Reported by: dimpase Owned by: AlexGhitza
Priority: major Milestone: sage-4.7.2
Component: coercion Keywords:
Cc: rkirov, SimonKing Merged in: sage-4.7.2.alpha3
Authors: Dmitrii Pasechnik Reviewers: William Stein
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Status badges

Description (last modified by leif)

print type(int(3) + 3)
print type(int(3) * 3)
print type(3 ^ int(3))
print type(int(3) ^ 3)

first three are Sage Integers but last one is just a Python int. Worse still:

sage: int(3)^-3 
0.037037037037037035 
sage: type(int(3)^-3) 
<type 'float'> 
sage: int(3)^QQ(-3) 
1/27 
sage: type(int(3)^QQ(-3)) 
<type 'sage.rings.rational.Rational'>

is very inconsistent, as well as leads to loss of precision, even though it can be avoided. As well as

sage: p(x)=x^-3
sage: p(int(3))
1/27

is not consistent.

Apply trac_11779.patch to the Sage library.

See https://groups.google.com/d/topic/sage-devel/RG-8xcPF53g/discussion.

Attachments (2)

trac_1779.patch (1.2 KB) - added by dimpase 11 years ago.
trac_11779.patch (1.4 KB) - added by dimpase 11 years ago.
slightly more optimized version

Download all attachments as: .zip

Change History (26)

Changed 11 years ago by dimpase

comment:1 Changed 11 years ago by dimpase

  • Cc rkirov added

comment:2 Changed 11 years ago by dimpase

  • Status changed from new to needs_review

comment:3 follow-up: Changed 11 years ago by leif

So what do you expect the result types to be?

IMHO the fact that 3r^3 yields a Python int is pretty correct; with negative exponents the interpretation is less straightforward.

I'd interpret int(x)^(-any) as (int(1)/int(x))^(any), so the result should be float if any is positive.

comment:4 in reply to: ↑ 3 ; follow-up: Changed 11 years ago by dimpase

Replying to leif:

So what do you expect the result types to be?

IMHO the fact that 3r^3 yields a Python int is pretty correct; with negative exponents the interpretation is less straightforward.

in all other operations (see the ticket description), the Sage type takes the precedence over Python. I certainly don't mind int(3)^int(3) being int though. in fact, with the patch applied, the behaviour is as follows:

sage: type(int(3)^3)
<type 'sage.rings.integer.Integer'>
sage: type(int(3)^int(3))
<type 'int'>
sage: type(int(3)^int(-3))
<type 'float'>
sage: type(int(3)^-3)
<type 'sage.rings.rational.Rational'> 

this is the behaviour that is much less confusing.

comment:5 in reply to: ↑ 4 ; follow-up: Changed 11 years ago by leif

  • Component changed from basic arithmetic to coercion

Replying to dimpase:

Replying to leif:

So what do you expect the result types to be?

IMHO the fact that 3r^3 yields a Python int is pretty correct; with negative exponents the interpretation is less straightforward.

in all other operations (see the ticket description), the Sage type takes the precedence over Python.

Exponentiation is different, i.e., the type of the exponent doesn't matter at all, only its value.


I certainly don't mind int(3)^int(3) being int though.

Why should one?


in fact, with the patch applied, the behaviour is as follows:

...
sage: type(int(3)^3)
<type 'sage.rings.integer.Integer'>
...

which is simply wrong, because 3r^3 is 3r * 3r * 3r, so the type of the result should be the base's type (unless the result is not an integer, in which case Python's rules should apply, leading to a float result.)

The following is equally wrong for the same reason. (This is a bug.)

sage: type(int(3)^-3)
<type 'sage.rings.rational.Rational'> 

this is the behaviour that is much less confusing.

Less confusing to whom? Honestly, every school child should know the difference.

Ok, one could argue that 3r * 3 could be interpreted as 3r + 3r + 3r as well (leading to result type int rather than Integer), but this would break commutativity.

Worth a discussion on sage-devel... (quite curious of the opinions :P )

comment:6 in reply to: ↑ 5 ; follow-up: Changed 11 years ago by leif

  • Authors set to Dmitrii Pasechnik
  • Cc SimonKing added

Replying to leif:

Replying to dimpase:

[...]

(This is a bug.)

sage: type(int(3)^-3)
<type 'sage.rings.rational.Rational'> 

I meant type(int(3)^QQ(-3)) not being float is a bug (currently in Sage).

We furthermore currently have:

sage: type(int(3)^QQ(-3))
<type 'sage.rings.rational.Rational'>
sage: type(int(3)^ZZ(-3))
<type 'float'>

which is obviously inconsistent, where IMHO the latter is correct, but not the former.

(Nevertheless, I know mathematicians tend to care less about types... ;-) )

comment:7 Changed 11 years ago by leif

Also

sage: type(int(3)^RR(-3))
<type 'sage.rings.real_mpfr.RealNumber'>

is wrong.

Worse:

sage: type(int(3)^RR(3))
<type 'sage.rings.real_mpfr.RealNumber'>

Much worse:

sage: type(int(3)^CC(3))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)

/tmp/Sage/sage-4.7.2.alpha2/devel/sage-11778/<ipython console> in <module>()

/tmp/Sage/sage-4.7.2.alpha2/local/lib/python2.6/site-packages/sage/rings/complex_number.so in sage.rings.complex_number.ComplexNumber.__pow__ (sage/rings/complex_number.c:6289)()

AttributeError: 'int' object has no attribute 'log'

comment:8 in reply to: ↑ 6 Changed 11 years ago by SimonKing

Replying to leif:

(Nevertheless, I know mathematicians tend to care less about types... ;-) )

Actually, I'd guess that mathematicians tend to prefer Sage Integer over Python int, since they are sceptical about int overflow...

comment:9 Changed 11 years ago by dimpase

  • Description modified (diff)

comment:10 Changed 11 years ago by dimpase

  • Description modified (diff)

comment:11 Changed 11 years ago by dimpase

  • Description modified (diff)

comment:12 Changed 11 years ago by dimpase

  • Description modified (diff)

comment:13 Changed 11 years ago by klee

I agree with leif. I think any in "int(3)any" should be converted to an integer n (or -n), and then int(3)any equals int(3) multiplied n times with itself (or numerical inverse of int(3) multiplied n times with itself). Thus int(3)any is of type float always. If any cannot be converted to an integer, then an exception should be raised.

Thus int(3)3, int(3)QQ(3), int(3)ZZ(3), int(3)CC(3) all results in Pythonic integer 27. int(3)-3, int(3)QQ(-3), int(3)ZZ(-3), int(3)CC(-3) all results in Pythonic float 0.037037037037037028.

By the way, I think there is nothing wrong with this

sage: p(x)=x^-3
sage: p(int(3))
1/27

because int(3) is converted to a Sage expression first. See

sage: q(x)=x
sage: type(q(int(3)))
<type 'sage.symbolic.expression.Expression'>

So I think this ticket and the patch is misdirected.

comment:14 follow-up: Changed 11 years ago by klee

(Sorry for badly formated comment) I agree with leif. I think any in int(3)^any should be converted to an integer n (or -n), and then int(3)^any equals int(3) multiplied n times with itself (or numerical inverse of int(3) multiplied n times with itself). Thus int(3)^any is of type float always. If any cannot be converted to an integer, then an exception should be raised.

Thus int(3)^3, int(3)^QQ(3), int(3)^ZZ(3), int(3)^CC(3) all result in Pythonic integer 27, and int(3)^-3, int(3)^QQ(-3), int(3)^ZZ(-3), int(3)^CC(-3) all result in Pythonic float 0.037037037037037028.

By the way, I think there is nothing wrong with this

sage: p(x)=x^-3
sage: p(int(3))
1/27

because int(3) is converted to a Sage expression first. See

sage: q(x)=x
sage: type(q(int(3)))
<type 'sage.symbolic.expression.Expression'>

So I think this ticket and the patch is misdirected.

comment:15 in reply to: ↑ 14 ; follow-up: Changed 11 years ago by dimpase

Replying to klee:

(Sorry for badly formated comment) I agree with leif. I think any in int(3)^any should be converted to an integer n (or -n), and then int(3)^any equals int(3) multiplied n times with itself (or numerical inverse of int(3) multiplied n times with itself).

What do you mean by "numerical inverse", and why you think this is the right precedence for operations? Leif here gives arguments based on algebraic properties, so it should be, for algebraic consistency that he cares about so much, that int(3)^-3==int(1)/int(3)^3, but this is not the case now, as Python 2 will convert 1/27 to 0.

I advocate the rule that a binary operation involving a Sage integer and a Python int should always produce a Sage type, as this is the case with all the other binary operations. Do you like this?

To me, your and Leif's arguments read as "I prefer the status quo to making Sage a more consistent system".

comment:16 in reply to: ↑ 15 ; follow-up: Changed 11 years ago by leif

Replying to dimpase:

Leif here gives arguments based on algebraic properties, so it should be, for algebraic consistency that he cares about so much, that int(3)^-3==int(1)/int(3)^3, but this is not the case now, as Python 2 will convert 1/27 to 0.

What makes you think I meant Python's broken / operator on ints? As I said on sage-devel, the behaviour of that operator on ints is useless since there's explicit truncating integer division already, namely //.

I advocate the rule that a binary operation involving a Sage integer and a Python int should always produce a Sage type, as this is the case with all the other binary operations.

The notion of "binary operations" has no meaning in this context, as the desired behaviour w.r.t. result types should depend on the definition of the function, especially its commonly used signature, not its arity.

For exponentiation, the type of the exponent is irrelevant, only its value matters to deduce the type of the result of the operation, just like for shift operations (which of course aren't commutative either).

Or would you say type( value << shift ) should depend on the type of shift?

This might perhaps look "consistent" from a syntactical point of view, but simply isn't sound, i.e. doesn't make any sense in the semantic domain.

To me, your and Leif's arguments read as "I prefer the status quo to making Sage a more consistent system".

Definitely not, since there are a lot of inconsistencies already which should IMHO be fixed, but not in the way you propose.

comment:17 in reply to: ↑ 16 ; follow-up: Changed 11 years ago by dimpase

Replying to leif:

Replying to dimpase:

Leif here gives arguments based on algebraic properties, so it should be, for algebraic consistency that he cares about so much, that int(3)^-3==int(1)/int(3)^3, but this is not the case now, as Python 2 will convert 1/27 to 0.

What makes you think I meant Python's broken / operator on ints? As I said on sage-devel, the behaviour of that operator on ints is useless since there's explicit truncating integer division already, namely //.

I advocate the rule that a binary operation involving a Sage integer and a Python int should always produce a Sage type, as this is the case with all the other binary operations.

The notion of "binary operations" has no meaning in this context, as the desired behaviour w.r.t. result types should depend on the definition of the function, especially its commonly used signature, not its arity.

well, being an algebraist by training and active in this research area for over 20 years, I tend to think that I know how to distinguish a binary operation from something else. And in my (not so in this case) humble opinion I think that exponentiation is a binary operation in this case.

For exponentiation, the type of the exponent is irrelevant, only its value matters to deduce the type of the result of the operation, just like for shift operations (which of course aren't commutative either).

Or would you say type( value << shift ) should depend on the type of shift?

it can, potentially; e.g. when shifting is not understood in a narrow context of binary words.

This might perhaps look "consistent" from a syntactical point of view, but simply isn't sound, i.e. doesn't make any sense in the semantic domain.

Look, I know an opinion on this issue of a lot of unhappy beginning users of Sage (the undergrads I currently teach), and they are dazed and confused by this inconsistency, among others. And I am merely trying to make Sage easier to use for them on this ticket.

comment:18 in reply to: ↑ 17 ; follow-up: Changed 11 years ago by klee

Replying to dimpase:

well, being an algebraist by training and active in this research area for over 20 years, I tend to think that I know how to distinguish a binary operation from something else. And in my (not so in this case) humble opinion I think that exponentiation is a binary operation in this case.

Then you know that multiplication is a basic binary operation of a ring, but exponentiation is simply a notation involving two values, one from the ring, the other from integers.

Look, I know an opinion on this issue of a lot of unhappy beginning users of Sage (the undergrads I currently teach), and they are dazed and confused by this inconsistency, among others. And I am merely trying to make Sage easier to use for them on this ticket.

The right direction for beginners is to let them know the difference between int(3) and 3 (Sage Integer) and that 3 behaves more mathematically than int(3) which suffers from numerical treatment.

comment:19 in reply to: ↑ 18 Changed 11 years ago by dimpase

Replying to klee:

Replying to dimpase:

well, being an algebraist by training and active in this research area for over 20 years, I tend to think that I know how to distinguish a binary operation from something else. And in my (not so in this case) humble opinion I think that exponentiation is a binary operation in this case.

Then you know that multiplication is a basic binary operation of a ring, but exponentiation is simply a notation involving two values, one from the ring, the other from integers.

Ever heard about exp()? Exponentiation is a jolly good binary operation on positive real numbers.

Look, I know an opinion on this issue of a lot of unhappy beginning users of Sage (the undergrads I currently teach), and they are dazed and confused by this inconsistency, among others. And I am merely trying to make Sage easier to use for them on this ticket.

The right direction for beginners is to let them know the difference between int(3) and 3 (Sage Integer) and that 3 behaves more mathematically than int(3) which suffers from numerical treatment.

Try to convince a beginner that the inconsistencies that got this ticket started are OK, without saying that Sage has not made a good choice.

comment:20 follow-up: Changed 11 years ago by was

  • Status changed from needs_review to needs_work

Can you also add a doctest that illustrates this:

   sage: type(int(3)^2)
   ...Integer...

I'm in favor of this change. It was always planned to add __pow__ to the coercion model, and have Sage types be favored over Python types (just as the are for the other arithmetic operations), but nobody got around to it. This patch doesn't make such a big sweeping change as adding __pow__ to the coercion model, but it is a step in the right direction.

Changed 11 years ago by dimpase

slightly more optimized version

comment:21 in reply to: ↑ 20 Changed 11 years ago by dimpase

  • Status changed from needs_work to needs_review

Replying to was:

Can you also add a doctest that illustrates this:

    sage: type(int(3)^2)
    ...Integer...

Done. I have also added type(int(3)^int(3)) doctest to show how to stick with 'int', if needed.

comment:22 Changed 11 years ago by was

  • Status changed from needs_review to positive_review

Looks good to me.

comment:23 Changed 11 years ago by leif

  • Description modified (diff)
  • Reviewers set to William Stein

Please don't put links to the HTML version of a patch into the description.

Trac wiki mark-up is [attachment:here_comes_the_filename].


Wonder whether your undergrads will complain that [1r][0] (or (1r,)[0]) doesn't yield a Sage Integer...

comment:24 Changed 11 years ago by leif

  • Merged in set to sage-4.7.2.alpha3
  • Resolution set to fixed
  • Status changed from positive_review to closed
Note: See TracTickets for help on using tickets.