Sympy: Lambdify with `modules='numpy'` fails when `auto_int_to_Integer=True`

Created on 23 Jul 2017  路  6Comments  路  Source: sympy/sympy

Every input in the sympy-live shell is processed using int_to_Integer (converts Python int to sympy Integers) because this makes it much nicer. Not sure but maybe some of the example in the docs also assume this behaviour.

While trying to understand some bugs in sympy-live related to numpy-lambdification, I was able to isolate the following bug that appears to exist in sympy (1.1 and 1.1.1rc1).

To reproduce, pip install sympy numpy then run

import numpy
import sympy
sympy.init_session(auto_int_to_Integer=True)
print 'using sympy', sympy.__version__, 'and numpy', numpy.__version__

wait a second then run

x = sympy.symbols('x')
expr = sympy.sin(x)
f = sympy.lambdify(x, expr, "numpy")
a = 3
print( f(3) )

This results in

using sympy 1.1 and numpy 1.13.1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-a8c650daaf7d> in <module>()
      3 f =sympy .lambdify (x ,expr ,"numpy")
      4 a =Integer (3 )
----> 5 print (f (Integer (3 )))

/int_to_Int_lambdify_bug/venv/lib/python2.7/site-packages/sympy/utilities/lambdify.pyc in wrapper(*argsx, **kwargsx)
    439             @wraps(funcarg)
    440             def wrapper(*argsx, **kwargsx):
--> 441                 return funcarg(*[namespace['asarray'](i) for i in argsx], **kwargsx)
    442             return wrapper
    443         func = array_wrap(func)

/int_to_Int_lambdify_bug/venv/lib/python2.7/site-packages/numpy/__init__.pyc in <lambda>(_Dummy_140)
AttributeError: 'Integer' object has no attribute 'sin'

I saw there is some work on lambdify-related work in 1.1.1 so I tried that too using

pip install git+git://github.com/sympy/[email protected]#egg=sympy

and running the same code as above produced

using sympy 1.1.1rc1 and numpy 1.13.1

In [2]: x = sympy.symbols('x')
   ...: expr = sympy.sin(x)
   ...: f = sympy.lambdify(x, expr, "numpy")
   ...: a = 3
   ...: print( f(3) )
   ...:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-a8c650daaf7d> in <module>()
      3 f =sympy .lambdify (x ,expr ,"numpy")
      4 a =Integer (3 )
----> 5 print (f (Integer (3 )))

/int_to_Int_lambdify_bug/venv/lib/python2.7/site-packages/sympy/utilities/lambdify.pyc in wrapper(*argsx, **kwargsx)
    442                 newargs = [asarray(i) if isinstance(i, integer_types + (float,
    443                     complex)) else i for i in argsx]
--> 444                 return funcarg(*newargs, **kwargsx)
    445             return wrapper
    446         func = array_wrap(func)

/int_to_Int_lambdify_bug/venv/lib/python2.7/site-packages/numpy/__init__.pyc in <lambda>(_Dummy_140)
AttributeError: 'Integer' object has no attribute 'sin'
SymPy Live utilities.lambdify

All 6 comments

It's because SymPy objects do not work in NumPy functions, and auto_int_to_Integer causes the f(3) to become f(Integer(3)) (here, f is essentially the same as np.sin).

Probably the most correct fix would be to modify the int -> Integer translation so that it only applies to int/int division. In other words, instead of wrapping integer literals with Integer, replace int/int division with a call to Rational. That would avoid issues where ints are used in other scenarios where Integer doesn't work.

So instead of

    >>> int_to_Integer('1.2 + 1/2 - 0x12 + a1')
    '1.2 +Integer (1 )/Integer (2 )-Integer (0x12 )+a1 '

we'll have

    >>> int_to_Integer('1.2 + 1/2 - 0x12 + a1')
    '1.2 + Rational(5, 2) - 0x12 + a1 '

this makes a lot of sense...

The code looks quite readable, so I'll try to implement that.

Would this break anything else? I never used auto_int_to_Integer=True on the command line, and it seems like it's just a minor convnience so this API change shouldn't impact too many people.

The whole point of it is to avoid the gotcha of 1/2 = 0.5 instead of Rational(1, 2). Python ints don't lose any information for any other operation, so they can be used instead of Integer.

The code looks quite readable, so I'll try to implement that.

I would suggest you to use ast transformations instead, as I did in the diofant. See linked pr.

Wrapping integer divisions would fail, when integer operation is in dividend or divisor, e.g., 2**3/7 and (3+5)/7, which is more common cases.

@ylemkimon, good catch, more deep tests for arguments of ast.Div required in such cases.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JohnStone2017 picture JohnStone2017  路  6Comments

yidingjiang picture yidingjiang  路  3Comments

siefkenj picture siefkenj  路  5Comments

haoranShu picture haoranShu  路  3Comments

JanVanDieBos picture JanVanDieBos  路  3Comments