Choosing between different expression factorizations in SymPy - python

Say I have an expression as follows:
a*b*c + b*c + a*d
One could factorize it as:
b*(a*c + c) + (a*d)
or as
c*(a*b + b) + (a*d)
or as
a*d + b*c*(a + 1)
among other possibilities.
For other expressions, the # of possibilities can be much larger.
My question is, does SymPy have any utility that allows the user to choose which of them to display? Is there a way to specify the common factor/s to use when factorizing / grouping terms in an expression?
EDIT: As #user772649 points out below, I can use collect for this. However, collect seems to give different outputs depending on the initial factorization of the mathematical expression e.g.:
a,b,c,d = symbols("a,b,c,d")
# These two equations are mathematically equivalent:
eq1 = a*b*c + b*c + a*d
eq2 = a*d + b*c*(a + 1)
print collect(eq1, a)
print collect(eq2, a)
prints:
a*(b*c + d) + b*c
a*d + b*c*(a + 1)
The equations eq1 and eq2 are mathematically equivalent, but collect outputs a different factorization for each of them, despite of the fact that the call to the collect command was the same for both. This brings me to the following two questions:
Is there a way to "expand" an expression before calling collect?
Is there a way of "collecting" (factoring an expression) in a way that is invariant to the initial factorization without having to expand the expression first?

use collect():
from sympy import *
a,b,c,d = symbols("a,b,c,d")
eq = a * b * c + b * c + a * d
print collect(eq, b)
print collect(eq, c)
print collect(eq, b*c)
the output is:
a*d + b*(c + a*c)
a*d + c*(b + a*b)
a*d + b*c*(1 + a)

One thing that might be nice is if collect would collect on previously grouped sub-expressions if more than one symbol is given. But either giving a product to collect on (as #HYRY showed) or something like the following is possible:
def separatevars_additively(expr, symbols=[]):
from sympy import factor_terms
free = set(symbols) or expr.free_symbols
d = {}
while free:
f = free.pop()
expr, dep = expr.as_independent(f, as_Add=True)
if dep.has(*free):
return None
d[f] = factor_terms(dep)
if expr:
d[0] = expr
return d
var('a:d')
eq = a*b*c + b*c + a*d
def do(i):
return sum(separatevars_additively(eq,[i]).values())
for i in eq.free_symbols:
print('%s: %s' % (i, do(i)))
gives
b: a*d + b*c*(a + 1)
a: a*(b*c + d) + b*c
c: a*d + b*c*(a + 1)
d: a*b*c + a*d + b*c

Related

SymPy division doesn't cancel what it can when using symbolic denominator

I have some code using sympy.solvers.solve() that basically leads to the following:
>>> k, u, p, q = sympy.symbols('k u p q')
>>> solution = (k*u + p*u + q)/(k+p)
>>> solution.simplify()
(k*u + p*u + q)/(k + p)
Now, my problem is that it is not simplified enough/correctly. It should be giving the following:
q/(k + p) + u
From the original equation q = (k + p)*(m - u) this is more obvious (when you solve it manually, which my students will be doing).
I have tried many combinations of sol.simplify(), sol.cancel(), sol.collect(u) but I haven't found what can make it work (btw, the collect I can't really use, as I won't know beforehand which symbol will have to be collected, unless you can make something that collects all the symbols in the solution).
I am working with BookWidgets, which automatically corrects the answers that students give, which is why it's important that I have an output which will match what the students will enter.
First things first:
there is no "standard" output to a simplification step.
if the output of a simplification step doesn't suit your need, you might want to manipulate the expression with simplify, expand, collect, ...
two or more sequences of operations (simplify, expand, collect, ...) might lead to different results, or might lead to the same results. It depends on the expression being manipulated.
Let me show you with your example:
k, u, p, q = symbols('k u p q')
solution = (k*u + p*u + q)/(k+p)
# out1: (k*u + p*u + q)/(k + p)
solution = solution.collect(u)
# out2: (q + u*(k + p))/(k + p)
num, den = fraction(solution)
# use the linearity of addition
solution = Add(*[t / den for t in num.args])
# out3: q/(k + p) + u
In the above code, out1, out2, out3 are mathematically equivalent.
Instead of spending time to simplify outputs, I would test for mathematical equivalence with the equals method. For example:
verified_solution = (k*u + p*u + q)/(k+p)
num, den = fraction(verified_solution)
first_studend_sol = Add(*[t / den for t in num.args])
print(verified_solution.equals(first_studend_sol))
# True
second_student_solution = q/(k + p) + u
print(verified_solution.equals(second_student_solution))
# True
third_student_solution = q/(k + p) + u + 2
print(verified_solution.equals(third_student_solution))
# False
It looks like you want the expression in quotient/remainder form:
>>> n, d = solution.as_numer_denom()
>>> div(n, d)
(u, q)
>>> _[0] + _[1]/d
q/(k + p) + u
But that SymPy function may give unexpected results when the symbol names are changed as described here. Here is an alternative (for which I did not find and existing function in SymPy) that attempts more a synthetic division result:
def sdiv(p, q):
"""return w, r if p = w*q + r else 0, p
Examples
========
>>> from sympy.abc import x, y
>>> sdiv(x, x)
(1, 0)
>>> sdiv(x, y)
(0, x)
>>> sdiv(2*x + 3, x)
(2, 3)
>>> a, b=x + 2*y + z, x + y
>>> sdiv(a, b)
(1, y + z)
>>> sdiv(a, -b)
(-1, y + z)
>>> sdiv(-a, -b)
(1, -y - z)
>>> sdiv(-a, b)
(-1, -y - z)
"""
from sympy.core.function import _mexpand
P, Q = map(lambda i: _mexpand(i, recursive=True), (p, q))
r, wq = P.as_independent(*Q.free_symbols, as_Add=True)
# quick exit if no full division possible
if Q.is_Add and not wq.is_Add:
return S.Zero, P
# check multiplicative cancellation
w, bot = fraction((wq/Q).cancel())
if bot != 1 and wq.is_Add and Q.is_Add:
# try maximal additive extraction
s1 = s2 = 1
if signsimp(Q, evaluate=False).is_Mul:
wq = -wq
r = -r
Q = -Q
s1 = -1
if signsimp(wq, evaluate=False).is_Mul:
wq = -wq
s2 = -1
xa = wq.extract_additively(Q)
if xa:
was = wq.as_coefficients_dict()
now = xa.as_coefficients_dict()
dif = {k: was[k] - now.get(k, 0) for k in was}
n = min(was[k]//dif[k] for k in dif)
dr = wq - n*Q
w = s2*n
r = s1*(r + s2*dr)
assert _mexpand(p - (w*q + r)) == 0
bot = 1
return (w, r) if bot == 1 else (S.Zero, p)
The more general suggestion from Davide_sd about using equals is good if you are only testing the equality of two expressions in different forms.

How can i change a variable name in a sympy expression

I am working on a project where I need to change all variables that are named 'a' to a new variable ai, where i is the order of the variable a in the expression. For instance if we use the expression: 1 + x + a + a ** 2, the output should be: 1 + x + a0 + a1 ** 2. Here is a code that I've written to solve this but it doesn't work, the expression remains unchanged.
import sympy.parsing.sympy_parser as sp1
import sympy as sp
I=sp1.parse_expr('1 + x + a + a**2', evaluate=False)
a,x=sp.symbols('a x')
def pre(expr):
i=0
for arg in sp.postorder_traversal(expr):
if arg==a:
tmp=sp.symbols('a'+str(i))
arg=tmp
print(arg)
i=i+1
pre(I)
print(I)
One way to achieve that is:
from sympy import Pow, Mul, Symbol, degree
def change_symbol(expr, a):
"""expr: the expression to modify
a: the symbol to look for and substitute
"""
# define a wild symbol to look for Symbols, Multiplications and Powers
# containing the specified symbol
w = Wild("w", properties=[
lambda t: isinstance(t, (Pow, Mul, Symbol)) and ((a in t.args) or (t == a))
])
# find the terms that satisfy the above criteria
terms = list(expr.find(w))
terms.sort(key=lambda t: degree(t), reverse=True)
# loop over those terms and performs the substitution with new symbols
name = a.name
for t in terms:
o = degree(t)
s = Symbol(name + "%s" % (o - 1))
expr = expr.subs(t, s**o)
return expr
change_symbol(I, a)
# out: a0 + a1**2 + x + 1
Your code did not work because you never changed the expression. When you say arg = tmp that assigns a value of tmp to arg but this does not update expr. #Davide_sd shows a way to recreate an expression with pieces that have been modified. You can also let replace do the traversal and let it replace a as it encounters it.
suffix = [0] #mutable suffix
def na():
rv = Symbol('a%s'%suffix[0])
suffix[0]+=1 # modify for next time
return rv
>>> a,x=var('a x')
>>> (1 + x + 2*a + a**2).replace(lambda x: x==a, lambda x: na())
a0**2 + 2*a1 + x + 1
Note that you said "order in expression" and coded as though you meant "order encountered" but in the polynomial sense, "higher order" terms will not necessarily appear later in the ordered terms. Note that a**2 appears before 2*a and that is why the replace gave it a value of a0:
>>> (1 + x + 2*a + a**2).args
(1, x, a**2, 2*a)

Is there a way to add my own identities to simplify a system of polynomial equations in sympy?

I have the following identity:
1 = a + b + c
Supose that I have the expression:
expr = x*(a + b + c)
It can be simplified as x.
Is there a way to declare it to SymPy so it can simplify them? Actually I do the job mannualy:
>>> import sympy
>>> sympy.vars("x a b c")
>>> expr = x*(a + b + c)
>>> expr.subs(a + b + c, 1)
x
The ratsimpmodprime function will work in your case. It simplifies a rational function with respect to some polynomials that are assumed to be equal to zero:
In [13]: a, b, c, x = symbols('a, b, c, x')
In [14]: polys = [a + b + c - 1]
In [15]: basis = groebner(polys).polys
In [16]: ratsimpmodprime(x*(a + b + c), basis)
Out[16]: x
https://docs.sympy.org/latest/modules/simplify/simplify.html#ratsimpmodprime

Collecting like term of an expression in Sympy

I am currently dealing with functions of more than one variable and need to collect like terms in an attempt to simplify an expression.
Say the expression is written as follows:
x = sympy.Symbol('x')
y = sympy.Symbol('y')
k = sympy.Symbol('k')
a = sympy.Symbol('a')
z = k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zEx = z.expand()
print type(z)
print type(zEx)
EDIT: Formatting to add clarity and changed the expression z to make the problem easier to understand.
Say z contains so many terms, that sifting through them by eye. and selecting the appropriate terms, would take an unsatisfactory amount of time.
I want to collect all of the terms which are ONLY a multiple of a**1. I do not care for quadratic or higher powers of a, and I do not care for terms which do not contain a.
The type of z and zEx return the following:
print type(z)
print type(zEx)
>>>
<class 'sympy.core.add.Add'>
<class 'sympy.core.mul.Mul'>
Does anyone know how I can collect the terms which are a multiple of a , not a^0 or a^2?
tl'dr
Where z(x,y) with constants a and k described by z and zEx and their type(): How can one remove all non-a terms from z AND remove all quadratic or higher terms of a from the expression? Such that what is left is only the terms which contain a unity power of a.
In addition to the other answers given, you can also use collect as a dictionary.
print(collect(zEx,a,evaluate=False)[a])
yields the expression
k*x**2 + k*y**2
In the end it is just an one-liner. #asmeurer brought me on the right track (check the comments below this post). Here is the code; explanations can be found below:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys
x, y, k, a = symbols('x y k a')
# modified string: I added a few terms
z = x*(k*a**9) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*(k*a**1) - k*a**3 + y*a**5
zmod = Add(*[argi for argi in z.args if argi.has(a)])
Then zmod is
a**9*k*x - a**8*k + a**5*y - a**3*k + a*k*x
So let's look at this more carefully:
z.args
is just a collection of all individual terms in your expression (please note, that also the sign is parsed which makes things easier):
(k*x**2, a**5*y, -a**3*k, -a**8*k, a*k*x, a**9*k*x, k**2*x*y, k**3*x**2*y)
In the list comprehension you then select all the terms that contain an a using the function has. All these terms can then be glued back together using Add which gives you the desired output.
EDIT
The above returns all all the expressions that contain an a. If you only want to filter out the expressions that contain a with unity power, you can use collect and Mul:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys
x, y, k, a = symbols('x y k a')
z2 = x**2*(k*a**1) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*k*a - k*a**3 + y*a**1
zc = collect(z2, a, evaluate=False)
zmod2 = Mul(zc[a], a)
then zmod2 is
a*(k*x**2 + k*x + y)
and zmod2.expand()
a*k*x**2 + a*k*x + a*y
which is correct.
With the updated z you provide I run:
z3 = k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zc3 = collect(z3.expand(), a, evaluate=False)
zmod3 = Mul(zc3[a], a)
and then obtain for zmod3.expand():
a*k*x**2 + a*k*y**2
Is this the result you were looking for?
PS: Thanks to #asmeurer for all these helpful comments!
To iterate over the terms of an expression use expr.args.
I'm unclear what a is supposed to be, but the collect function may do what you want.

python sympy simplify and Eq

This may not be a question, just an observation, but is sympy supposed to work this way.
I have a two complicated expression, A and E, and I am trying to find out if they are equivalent. If I simplify one, say E, and use Eq(A,E) it does not return True, but the two separated with "==". If would have expected that sympy would be smart enough to work out they are equal. Eq(simplify(A),E) returns True. Here is the code ...
from sympy import *
B = symbols('B')
C = symbols('C')
F = symbols('F')
G = symbols('G')
H = symbols('H')
A = (B - C)*(G*(B + C) - (B - C - F)*H)**2
D = 2*(B**2+B*F-C**2)**2
E = A/D
ED=simplify(E*D)
print("E*D= {0}").format(str(ED))
print("A = {0}").format(str(A))
print("0 = {0}").format(str(simplify(A-ED)))
print("T = {0}").format(Eq(A,ED))
print("T = {0}").format(Eq(simplify(A),ED))
and the output
E*D= (B - C)*(G*(B + C) + H*(-B + C + F))**2
A = (B - C)*(G*(B + C) - H*(B - C - F))**2
0 = 0
T = (B - C)*(G*(B + C) - H*(B - C - F))**2 == (B - C)*(G*(B + C) + H*(-B + C + F))**2
T = True
Note the -H versus +H in the last expression.
Equality does not do any simplification and two objects are identical only if they are structurally (not mathematically) zero. Proving mathematical equality (in general) is not a simple problem so if they aren't identical (as in this case) SymPy doesn't even begin chasing the "equality rabbit" to its hole :-). This is the expected behavior. If you want to let SymPy try some simplification on its own, try using the equals method:
>>> A.equals(simplify(E*D))
True

Categories

Resources