Question

I have an expressions like:

a*b*c + d*e + f - g*h*h + i*a

In other words: terms can be either added or subtracted, and each term is a product of some of the symbols.

Is there a way to come up with a minimal/simpler expression, basically the reverse of expand? I tried simplify and factor but I cannot get them to work. For example:

a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4

should turn into:

(a - b)**4 - a

but it stays unchanged when using the said commands.

PS: If this is something what SymPy simply cannot do, could you please suggest an alternative which does this?

Was it helpful?

Solution

See sympy factor simple relationship. SymPy's factor only knows how to factor the entire expression, but if you know the term you want to rewrite things in terms of, you can use a trick with subs, like:

>>> expr = a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4
>>> expr.subs(a, x + b).expand()
-b + x**4 - x
>>> expr.subs(a, x + b).expand().subs(x, a - b)
-a + (a - b)**4

Basically, let x = a - b, so that a = x + b. Then replace a with x + b, expand things out, and replace it back.

For your more complicated example, SymPy is actually smart enough to replace a*b in an expression correctly:

>>> expr = (a*b - c*d)**2 - a
>>> expr = expr.expand()
>>> expr
a**2*b**2 - 2*a*b*c*d - a + c**2*d**2
>>> expr.subs(a*b, x + c*d)
-a + c**2*d**2 - 2*c*d*(c*d + x) + (c*d + x)**2
>>> expr.subs(a*b, x + c*d).expand()
-a + x**2
>>> expr.subs(a*b, x + c*d).expand().subs(x, a*b - c*d)
-a + (a*b - c*d)**2

Another possible approach to this problem would be to try using factor on subsets of the terms in an expression (itertools.combinations could be useful here). For instance, to try factoring all combinations of all terms but one from your original expression:

>>> args = Add.make_args(expr)
>>> for comb in combinations(args, len(args) - 1):
...    print(factor(Add(*comb)) + Add(*(set(args) - set(comb))))
...
a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4
a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4
a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4
a**4 - 4*a**3*b + 6*a**2*b**2 - 4*a*b**3 - a + b**4
-a + (a - b)**4
a*(a**3 - 4*a**2*b + 6*a*b**2 - 4*b**3 - 1) + b**4

You could check not isinstance(factored_expr, Add) to filter out the ones that aren't factored.

OTHER TIPS

If you know the function in advance, then you can use more powerful software packages such as Maple to reduce the expression before putting it into your computer code. There is a optimization package in Maple, which reduces the expression into sub-expressions such that it takes advantage of repeated operations in the expression. Also you can factorize very complicated expressions in a much reliable way.

In addition such software also can create programming code as output, which you can directly paste in your program. If you do not have access to Maple or Mathematica software, you can also use a free (but powerful) software called maxima. http://maxima.sourceforge.net/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top