how to get SymPy to replace ints like 1.0 with 1

Viewed 256

Sorry for the newbie question but I have not found the answer elsewhere so far.

Say we have a symbol and we multiply it by the float number one:

import sympy

x = sympy.Symbol('x')
x = 1.0 * x

All the expressions that contain this x will also contain factor one whatever I do:

from pprint import pprint
pprint(x)
pprint(sympy.simplify(x))

Output:

1.0⋅x
1.0⋅x

In the example above, I want to output x instead of 1.0⋅x. Is it possible to simplify the expression in the way that it solves all multiplications by a float number one? Should I transform this float number to another datatype?

3 Answers

Looks like it's due to the floating-point vs integer 1 - don't use 1.0 if you can get away with 1, perhaps?

You could check if it's identically 1.0 with if f.as_integer_ratio() == (1, 1), and if so don't prepend the unnecessary '1.0':

>>> xf = 1.0 * x
>>> xi = 1 * x

>>> xf
1.0*x
>>> xi
x

>>> pprint(xf)
1.0*x
>>> pprint(xi)
x

It might be due to floating-point inexactness (I don't know sympy much, can't say more).

EDIT: the runtime of float.as_integer_ratio() is tiny, in response to your question:

>>> python -m timeit -n 1000000 '(1.0).as_integer_ratio() == (1,1)'
1000000 loops, best of 5: 172 nsec per loop

>>> python -m timeit -n 1000000 '(1.1).as_integer_ratio() == (1,1)'
1000000 loops, best of 5: 327 nsec per loop

>>> python -m timeit -n 1000000 '(1.01).as_integer_ratio() == (1,1)'
1000000 loops, best of 5: 331 nsec per loop

>>> python -m timeit -n 1000000 '(0.999999999999).as_integer_ratio() == (1,1)'
1000000 loops, best of 5: 329 nsec per loop

I have found a solution. There is a special function nsimplify() that does the job.

If nsimplify is more aggressive that you intend -- it will simplify Floats to values that are close but not exactly the same -- then you might consider the following function which will only target integers expressed as Floats.

>>> def flint(eq):
...     """convert floats that are ints to ints"""
...     reps = {}
...     e = eq.replace(
...         lambda x: x.is_Float and x == int(x),
...         lambda x: reps.setdefault(x, Dummy()))
...     return e.xreplace({v: int(k) for k, v in reps.items()})
>>> flint(1.0*x)
x
>>> flint(1.0*x+x**2.0)
x**2 + x
Related