Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

shortcut coercion for Integer-Rational operations #20731

Closed
videlec opened this issue May 31, 2016 · 59 comments
Closed

shortcut coercion for Integer-Rational operations #20731

videlec opened this issue May 31, 2016 · 59 comments

Comments

@videlec
Copy link
Contributor

videlec commented May 31, 2016

Doing some shortcut to coercion actually fasten the code by a factor 3...

Old version

sage: z = 2
sage: q = QQ((-1,5))
sage: %timeit z + q
1000000 loops, best of 3: 930 ns per loop
sage: %timeit q + z
1000000 loops, best of 3: 930 ns per loop
sage: %timeit z - q
1000000 loops, best of 3: 848 ns per loop
sage: %timeit q - z
1000000 loops, best of 3: 776 ns per loop
sage: %timeit z * q
1000000 loops, best of 3: 1.15 µs per loop
sage: %timeit q * z
1000000 loops, best of 3: 1.17 µs per loop
sage: %timeit z / q
1000000 loops, best of 3: 1.09 µs per loop
sage: %timeit q / z
1000000 loops, best of 3: 1.1 µs per loop

New version

sage: z = 2
sage: q = QQ((-1,5))
sage: %timeit z + q
1000000 loops, best of 3: 265 ns per loop
sage: %timeit q + z
1000000 loops, best of 3: 323 ns per loop
sage: %timeit z - q
1000000 loops, best of 3: 261 ns per loop
sage: %timeit q - z
1000000 loops, best of 3: 319 ns per loop
sage: %timeit z * q
1000000 loops, best of 3: 255 ns per loop
sage: %timeit q * z
1000000 loops, best of 3: 332 ns per loop
sage: %timeit z / q
1000000 loops, best of 3: 289 ns per loop
sage: %timeit q / z
1000000 loops, best of 3: 314 ns per loop

CC: @jdemeyer

Component: coercion

Keywords: days74

Author: Vincent Delecroix

Branch/Commit: d535aee

Reviewer: Jeroen Demeyer, Travis Scrimshaw

Issue created by migration from https://trac.sagemath.org/ticket/20731

@videlec videlec added this to the sage-7.3 milestone May 31, 2016
@videlec

This comment has been minimized.

@videlec
Copy link
Contributor Author

videlec commented May 31, 2016

Branch: u/vdelecroix/20731

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented May 31, 2016

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

33f9fd4Trac 20731: shortcut coercion for Integer-Rational

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented May 31, 2016

Commit: 33f9fd4

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented May 31, 2016

Changed commit from 33f9fd4 to 19c075b

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented May 31, 2016

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

19c075bTrac 20731: fix doctests

@jdemeyer
Copy link

jdemeyer commented Jun 1, 2016

comment:4

Move src/sage/rings/binop.pyx to src/sage/libs/gmp since it has nothing to with rings. And I would just use a .pxd file with inline functions, no .pyx file.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 1, 2016

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

fdf79acTrac 20731: get rid of ZZ._div

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 1, 2016

Changed commit from 19c075b to fdf79ac

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 1, 2016

Changed commit from fdf79ac to eec4168

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 1, 2016

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

9078830Trac 20731: moving binop
eec4168Trac 20731: fix the move

@jdemeyer
Copy link

jdemeyer commented Jun 1, 2016

comment:7

I think that mpq_mul_z and mpq_div_z are overkill. Why don't you just multiply the numerator or denominator with the integer and call mpq_canonicalize, similar to what you do for Integer/Integer division?

@videlec
Copy link
Contributor Author

videlec commented Jun 1, 2016

comment:8

QQ((2^100, 3^100)) * 3^100

@jdemeyer
Copy link

jdemeyer commented Jun 1, 2016

comment:9

Right, but that's a rather artificial example. What about simple cases?

@jdemeyer
Copy link

jdemeyer commented Jun 1, 2016

comment:10

In src/sage/repl/interpreter.py, there is a good reason that the line numbers were changed to ...

@jdemeyer
Copy link

jdemeyer commented Jun 1, 2016

comment:11

I just thought about the following very simple algorithm for the multiplication (division is analogous):

To compute (A/B) * C, you initialize the result as C/B, canonicalize it and then multiply the numerator by A.

You don't need to canonicalize the final result, since gcd(A,B) = 1 by assumption.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 2, 2016

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

72f77b9Trac 20731: simpler operations
c7a8d8e20731: simpler mpq_mul_z/mpq_div_z

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jun 2, 2016

Changed commit from eec4168 to c7a8d8e

@jdemeyer
Copy link

jdemeyer commented Jun 2, 2016

comment:14

[comment:10]

@tscrim
Copy link
Collaborator

tscrim commented Jun 26, 2016

comment:35

Replying to @videlec:

Replying to @tscrim:

I'm not quite sure without looking through it. It might be something at a lower level than Sage.

I really do not understand this comment.

This was in reference to comment:31, where I was thinking the issue with the time difference might be something in mpz instead of something we are doing in Sage. It might be better to take the faster version and utilize that addition/multiplication is commutative to get that extra little bit of speed.

@videlec
Copy link
Contributor Author

videlec commented Jun 27, 2016

comment:36

Replying to @tscrim:

Replying to @videlec:

Replying to @tscrim:

I'm not quite sure without looking through it. It might be something at a lower level than Sage.

I really do not understand this comment.

This was in reference to comment:31, where I was thinking the issue with the time difference might be something in mpz instead of something we are doing in Sage. It might be better to take the faster version and utilize that addition/multiplication is commutative to get that extra little bit of speed.

There is no fundamental difference between z+q and q+z. The code is copy paste calling the very same functions. comment:31 is just something that I do not understand.

@tscrim
Copy link
Collaborator

tscrim commented Jun 27, 2016

Reviewer: Jeroen Demeyer, Travis Scrimshaw

@tscrim
Copy link
Collaborator

tscrim commented Jun 27, 2016

comment:37

Ah, I see it now. I guess the only difference is checking isinstance for Integer is longer than that of Rational because of a longer MRO:

sage: p = 2
sage: p.__class__.__mro__
(<type 'sage.rings.integer.Integer'>,
 <type 'sage.structure.element.EuclideanDomainElement'>,
 <type 'sage.structure.element.PrincipalIdealDomainElement'>,
 <type 'sage.structure.element.DedekindDomainElement'>,
 <type 'sage.structure.element.IntegralDomainElement'>,
 <type 'sage.structure.element.CommutativeRingElement'>,
 <type 'sage.structure.element.RingElement'>,
 <type 'sage.structure.element.ModuleElement'>,
 <type 'sage.structure.element.Element'>,
 <type 'sage.structure.sage_object.SageObject'>,
 <type 'object'>)
sage: q = 2/3
sage: q.__class__.__mro__
(<type 'sage.rings.rational.Rational'>,
 <type 'sage.structure.element.FieldElement'>,
 <type 'sage.structure.element.CommutativeRingElement'>,
 <type 'sage.structure.element.RingElement'>,
 <type 'sage.structure.element.ModuleElement'>,
 <type 'sage.structure.element.Element'>,
 <type 'sage.structure.sage_object.SageObject'>,
 <type 'object'>)

I'm happy with this ticket as-is. Jeroen, any more comments?

@videlec
Copy link
Contributor Author

videlec commented Jul 8, 2016

comment:38

One way to shortcut the isinstance(right, Rational) is to instead do type(right) is Rational. That would actually be the fastest. (And it will still work with an element that inherits from Rational since _add_, _mul_ etc are properly implemented.) What do you think?

@tscrim
Copy link
Collaborator

tscrim commented Jul 9, 2016

comment:39

I was originally thinking that we should keep it as isinstance, but since we explicitly construct a new Rational (and I don't think it could be made to work with subtypes [easily]), I think we should only test against Rational. Plus those extra little nanoseconds can matter at this level. Subclasses can just fall into the coercion framework.

@videlec
Copy link
Contributor Author

videlec commented Jul 9, 2016

comment:40

hum... with isinstance(X,Y)

sage: %timeit z+q                                                              
1000000 loops, best of 3: 242 ns per loop
sage: %timeit q+z                                                              
1000000 loops, best of 3: 306 ns per loop

with type(X) is Y

sage: %timeit z+q
1000000 loops, best of 3: 243 ns per loop                                      
sage: %timeit q+z
1000000 loops, best of 3: 334 ns per loop

@videlec
Copy link
Contributor Author

videlec commented Jul 9, 2016

comment:41

Independent time testing with a Cython class (without inheritance)

code relative time
type(a) == A 10500000ns
isinstance(a,A) 559000ns
type(a) is A 55ns

I guess something else is happening with z+q versus q+z...

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jul 9, 2016

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

dd2defbTrac 20731: isinstance(X,Y) -> type(X) is Y

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jul 9, 2016

Changed commit from 1a93a12 to dd2defb

@tscrim
Copy link
Collaborator

tscrim commented Jul 10, 2016

comment:43

That really suggests that it has to do with the MRO length (comment:37), but I am somewhat surprised that does matter. Well, this is a definite improvement and should get into 7.3.

@vbraun
Copy link
Member

vbraun commented Jul 10, 2016

comment:44
sage -t --long src/sage/repl/interpreter.py
**********************************************************************
File "src/sage/repl/interpreter.py", line 77, in sage.repl.interpreter
Failed example:
    shell.run_cell('1/0')
Expected:
    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-...> in <module>()
    ----> 1 Integer(1)/Integer(0)
    <BLANKLINE>
    .../src/sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (build/cythonized/sage/rings/integer.c:...)()
       ...          if type(left) is type(right):
       ...              if mpz_sgn((<Integer>right).value) == 0:
    -> ...                  raise ZeroDivisionError("rational division by zero")
       ...              x = <Rational> Rational.__new__(Rational)
       ...              mpq_div_zz(x.value, (<Integer>left).value, (<Integer>right).value)
    <BLANKLINE>
    ZeroDivisionError: rational division by zero
Got:
    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-1-6f88eab09598> in <module>()
    ----> 1 Integer(1)/Integer(0)
    <BLANKLINE>
    /home/buildslave-sage/slave/sage_git/build/src/sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (/home/buildslave-sage/slave/sage_git/build/src/build/cythonized/sage/rings/integer.c:12882)()
       1841         if type(left) is type(right):
       1842             if mpz_sgn((<Integer>right).value) == 0:
    -> 1843                 raise ZeroDivisionError("rational division by zero")
       1844             x = <Rational> Rational.__new__(Rational)
       1845             mpq_div_zz(x.value, (<Integer>left).value, (<Integer>right).value)
    <BLANKLINE>
    ZeroDivisionError: rational division by zero
**********************************************************************
1 item had failures:
   1 of   5 in sage.repl.interpreter
    [133 tests, 1 failure, 3.60 s]

@videlec
Copy link
Contributor Author

videlec commented Jul 11, 2016

comment:45

Dear Volker,

I am not able to reproduce this... could you tell us on which kind of configuration you obtained that?

Vincent

@tscrim
Copy link
Collaborator

tscrim commented Jul 15, 2016

comment:46

I think the test is just fragile:

    .../src/sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (build/cythonized/sage/rings/integer.c:...)()

vs the actual output:

    /home/buildslave-sage/slave/sage_git/build/src/sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (/home/buildslave-sage/slave/sage_git/build/src/build/cythonized/sage/rings/integer.c:12882)()

Note the path is .../build/src/... and compare with the previous doctest output format.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jul 22, 2016

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

d535aeeTrac 20731: fix doctest

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Jul 22, 2016

Changed commit from dd2defb to d535aee

@vbraun
Copy link
Member

vbraun commented Jul 23, 2016

Changed branch from u/vdelecroix/20731 to d535aee

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants