3

I am working with some long equations but not really complex, and I wanted to use sympy to simplify and "factorize" them. But I have encountered a few problems. Here is a list of some minimal examples:

Problem 1: symmetry

from sympy import *
from __future__ import division
a = symbols('a')
b = symbols('b')
expr = 1/12*b + 1
expr.subs(1/12*b, a)
expr.subs(b*1/12, a)

The first line gives the expected result (ie. a+1) while in the second one there is no substitution.

Problem 2: factorized expressions

Some parts of the expression are factorized and when I expand the expression they get simplified, thus making the substitution impossible. For example

(((x+1)**2-x).expand()).subs(x**2+2*x, y+1)

will give x^2+x+1 and what I am looking for is y+2-x.

Question

Is there a way to solve these problems ? Or maybe I should use another symbolic mathematical tool ? Any suggestions are welcomed.

lasofivec
  • 331
  • 1
  • 4
  • 11

3 Answers3

7

There is a major gotcha in SymPy, which is that, because of the way Python works, number/number gives a floating point (or does integer division if you use Python 2 and don't from __future__ import division).

In the first case and in your original expression, Python evaluates 1/12*b from left to right. 1/12 is evaluated by Python to give 0.08333333333333333, which is then multiplied by b. In the second case, b*1 is evaluated as b. Then b/12 is evaluated by SymPy (because b is a SymPy object), to give Rational(1, 12)*b.

Due to the inexact nature of floating point numbers, SymPy does not see the float 0.08333333333333333 as equal to the rational 1/12.

There is some more discussion of this issue here. As a workaround, you should avoid direct integer/integer without wrapping it somehow, so that SymPy can create a rational. The following will all create a rational:

b/12
Rational(1, 12)*b
S(1)/12*b

For (((x+1)**2-x).expand()).subs(x**2+2*x, y+1) the issue is that x**2 + 2*x does not appear exactly in the expression, which is x**2 + x + 1. SymPy generally only replaces things that it sees exactly.

It seems you don't mind adding and subtracting an x to make the replacement work. So I would suggest doing instead (((x+1)**2-x).expand()).subs(x**2, y+1 - 2*x). By only substituting a single term (x**2), the substitution will always work, and the 2*x will cancel out to leave whatever x term remains (in this case, -x).

asmeurer
  • 86,894
  • 26
  • 169
  • 240
1

Here's a possible solution to your problems:

from sympy import *

a = symbols('a')
b = symbols('b')
expr = 1 / 12 * b + 1
print(expr.subs((1 / 12) * b, a))
print(expr.subs(b * (1 / 12), a))

x = symbols('x')
y = symbols('y')
expr = ((x + 1)**2 - x).expand()
print(expr.subs(x**2 + x, y - x + 1))
BPL
  • 9,632
  • 9
  • 59
  • 117
  • For the first problem I think there is a `from __future__ import division `, with this addition I tried and it's working. However, I don't understand why. For the second problem, this really doesn't solve my problem. What I gave here are minimal examples but in reality the equations are really long. I cannot really 'cheat' the results. And I definitely can't change the value to be substituted... Thanks for answering ! – lasofivec Sep 05 '16 at 16:07
  • For the second question maybe my question is "is it possible to expand without simplifying" ? – lasofivec Sep 05 '16 at 16:10
1

Regarding problem 1, note that 1/12*b and b*1/12 are not the same thing in sympy. The first is a floating number mutliplied by a symbol, whereas the second is an exact symbolic expression (you can check it out by a simple print statement). Since expr contains 1/12*b, it is not surprising that the second subs does not work.

Regarding problem 2, the subs rule you provide is ambiguous. In particular the substitution rule implies that equation x**2+2*x==y+1. However, this equation has many interpretations, e.g,

x**2 == y + 1 - 2*x (this is the one you consider),

x**2 + x == y + 1 - x,

x == (y + 1 - x**2)/2,

For this reason, I consider sympy refusing to perform a substitution is actually a correct approach.

If it is the first interpretation you want, it is better to explicitly provide it in the subs rule, i.e.,

(((x+1)**2-x).expand()).subs(x**2, -2*x + y + 1)

-x + y + 2

Stelios
  • 5,271
  • 1
  • 18
  • 32